大名鼎鼎的aiohttp,相信如果你学习Python或者爬虫的时候,肯定听说过这个东西。没听过也不要紧,今天看完文章,只要记住,aiohttp这个东西,在写爬虫的时候,很牛逼就行了。

aiohttp 就是一个用 asyncio实现的 HTTP client/server。 你可以通过它来简单实现一个具有异步处理功能的 clients 和 servers。 aiohttp同时还支持 Server WebSockets 和 Client WebSockets,让你使用起来更加简单。

今天,皮爷就带你来体验一下,这个“爬虫加速器”。

0x00 我们的爬虫需求

皮爷最近在做一个项目,就是用微信小程序追美剧的项目,那么首先,我们得需要有一个所有美剧的来源,恰巧,下面这个排行榜就有我们所有需要的信息:

http://www.ttmeiju.vip/index.php/summary/index/p/1.html

初级要求

我们很简单,就是需要从【第一页】:

http://www.ttmeiju.vip/index.php/summary/index/p/1.html

一直爬到最后一页【第三十五页】:

http://www.ttmeiju.vip/index.php/summary/index/p/35.html

中级要求

由于排行榜页面没有美剧的【季】信息,这个必须进入详情页来做,所以,中级要求就是针对每一条美剧,进入详情页,从里面爬取出来当前美剧的【季】信息。

这个要求不难吧?就是一级页面变换 page number 的数值来爬取信息。就算要爬取【季】信息,我们的爬虫深度也就才两级。

所以,这个需求不难。而且网页都是静态资源,一般简单的爬虫程序就能hou住。

0x01 撸码前的整理

这一步,我们需要想想通过什么样的方法能够实现上面的需求。

熟悉皮爷的童鞋都知道,皮爷之前的爬虫程序主要用 Scrapy 这个框架。为啥主要用这个?主要这个是一个框架。框架的意思就是写起来简单。何为简单?你只需要专注写爬虫的相关逻辑部分就好,不需要管理程序的生命周期,代码控制之类的问题,因为框架都给你整理好了。

那么,我们的需求就可以用两种做法来搞:

用 Scrapy 来写。自己写爬虫,但是要用到 aiohttp 的东西。下面皮爷就简单为大家来说一下他们是怎么实现的,以及最后对比结果。

0x02 Scrapy撸发撸起来

scrapy的写法,皮爷之前写过很多遍了,具体的教学文章,可以参考皮爷之前写的:

基于云服务的网站种子采集器,还能发送到邮箱,你不来考虑一下?

这里,我们就直接开始说具体的实现代码了。代码实现的就是从1页爬取到35页面,先不考虑“两层爬取”的数据。

class TtmjspiderSpider(scrapy.Spider):name = '皮爷spider' root_url = "http://www.ttmeiju.vip" def start_requests(self): start_url = "http://www.ttmeiju.vip/index.php/summary/index/p/1.html" yield Request(url=start_url, callback=self.parse_page, dont_filter=True, meta={"cur_page": 1, "max_page_num": -1}) def parse_page(self, response):content = response.body soup = BeautifulSoup(content, "html.parser")cur_page = response.meta["cur_page"] cur_url = response.urlmax_page_num = response.meta["max_page_num"] # 第一页找top3的标签rank_top_3_div = soup.find_all(name="div", attrs={"class": "ranktop3"}) for item in rank_top_3_div: link_a = item.find_all(name="a")[0] tv_url = self.root_url + link_a["href"] tv_name = link_a.text tv_rank_num = item.find_all(name="div", attrs={"class": "ranknum"})[0].text play_info_div = item.find_all(name="div", attrs={"class": "mjinfo"}) play_info_one = play_info_div[0].text play_info_two = play_info_div[1].text tv_category = play_info_one.split("/")[0].strip() tv_status = play_info_one.split("/")[1].strip()tv_update_day = play_info_one.split("/")[2].split(":")[-1].strip() temp_result = re.findall("\d{4}-\d{2}-\d{2}", play_info_two) if len(temp_result) != 0:tv_return_date = temp_result[0] else: tv_return_date = "暂无" # 构建 itemtv_item = TtmjTvPlayItem() tv_item["tv_play_name"] = tv_nametv_item["tv_play_rank"] = int(tv_rank_num) tv_item["tv_play_category"] = tv_category tv_item["tv_play_state"] = tv_statustv_item["tv_play_update_day"] = tv_update_daytv_item["tv_play_return_date"] = tv_return_date tv_item["tv_play_url"] = tv_url tv_item["tv_play_cur_season"] = 0 yield tv_item # 正常信息列表content_div = soup.find_all(name="tr", attrs={"class": re.compile(r"Scontent")}) for item in content_div: td_list = item.find_all(name="td") tv_rank_num = td_list[0].text link_a = td_list[1].find(name="a") tv_url = self.root_url + link_a["href"] tv_name = link_a.text tv_category = td_list[2].text.strip() tv_status = td_list[3].text.strip()tv_update_day = td_list[4].text.strip() tv_return_date = td_list[5].text.strip()tv_item = TtmjTvPlayItem() tv_item["tv_play_name"] = tv_nametv_item["tv_play_rank"] = int(tv_rank_num) tv_item["tv_play_category"] = tv_category tv_item["tv_play_state"] = tv_statustv_item["tv_play_update_day"] = tv_update_daytv_item["tv_play_return_date"] = tv_return_date tv_item["tv_play_url"] = tv_url tv_item["tv_play_cur_season"] = 0 yield tv_item next_page_ul = soup.find_all(name="ul", attrs={"class": "pagination"}) if len(next_page_ul) != 0: last_page_a = next_page_ul[0].find_all(name="a", attrs={"class": "end"}) if len(last_page_a) != 0 and max_page_num == -1: max_page_num = last_page_a[0].text if int(cur_page) < int(max_page_num):next_page_num = int(cur_page) + 1 else: logging.info("ALl finished") returnnext_page_url = cur_url[:-(len(cur_url.split("/")[-1]))] + str(next_page_num) + ".html" yield Request(url=next_page_url, callback=self.parse_page, dont_filter=True, meta={"cur_page": next_page_num, "max_page_num": max_page_num})

代码简单说一下,通过 【Chrome】--【检查】页面,看到我们要找的列表信息标签。

然后,通过 BeautifulSoup 来解析找到相对应的文字,并且解析成我们想要得到的 Scrapy Item ,最后在 pipeline 里面做存入数据库的操作。

那我们接下来就运行一下这个 Scrapy 框架写的爬取 35 页信息的爬虫,看看效果如何。

数据库里面看到已经存入了数据:

从结果里面看到,用 Scrapy ,没有修改 setting.py 文件,爬取 35 页数据,然后生成 Scrap.Item ,总共用了 2 分 10 秒。成绩还可以哈。

0x03 aiohttp撸法撸起来

这里,皮爷用网上的一张图来给大家看一下 aiohttp 的流程:

其实 aiohttp 就是讲事件进入一个队列,然后挨个调用执行,这些任务有个共同的特点,就是他们需要等待操作。所以,在等待的过程中,程序会调起其他任务接着执行。

我们来看代码:

sem = asyncio.Semaphore(80) # 信号量,控制协程数,防止爬的过快client = pymongo.MongoClient("mongodb://xx.xx.xx.xx/", xxx)db = client["xxx"]ttmj_collection = db["xxx"]result_dict = list()def generateRequestList(url, start, end):page_list = list() for i in range(start, end): genUrl = url.replace("**", str(i)) page_list.append(genUrl) return page_listasync def grab_page(url): with(await sem): async with aiohttp.ClientSession() as session: content = await fetch(session, url, 0)async def fetch(session, url, level, tv_item=None): async with session.get(url) as req: content = await req.text() soup = BeautifulSoup(content, "html.parser") root_url = "http://www.ttmeiju.vip"cur_time_string = datetime.datetime.now().strftime('%Y-%m-%d')rank_top_3_div = soup.find_all(name="div", attrs={"class": "ranktop3"}) for item in rank_top_3_div: link_a = item.find_all(name="a")[0] tv_url = root_url + link_a["href"] tv_name = link_a.text tv_rank_num = item.find_all(name="div", attrs={"class": "ranknum"})[0].text play_info_div = item.find_all(name="div", attrs={"class": "mjinfo"}) play_info_one = play_info_div[0].text play_info_two = play_info_div[1].text tv_category = play_info_one.split("/")[0].strip() tv_status = play_info_one.split("/")[1].strip()tv_update_day = play_info_one.split("/")[2].split(":")[-1].strip() temp_result = re.findall("\d{4}-\d{2}-\d{2}", play_info_two) if len(temp_result) != 0:tv_return_date = temp_result[0] else: tv_return_date = "暂无" tv_item = TtmjTvPlayItem() tv_item.tv_play_name = tv_name tv_item.tv_play_rank = int(tv_rank_num) tv_item.tv_play_category = tv_categorytv_item.tv_play_state = tv_status tv_item.tv_play_update_day = tv_update_day tv_item.tv_play_return_date = tv_return_datetv_item.tv_play_update_time = cur_time_string tv_item.tv_play_url = tv_urltv_item_dict = dict( (name, getattr(tv_item, name)) for name in dir(tv_item) if not name.startswith('__')) # print("complete Item: %s" % (tv_item.tv_play_name)) result_dict.append(tv_item_dict) # await fetch(session, tv_url, 1, tv_item) content_div = soup.find_all(name="tr", attrs={"class": re.compile(r"Scontent")}) for item in content_div: td_list = item.find_all(name="td") tv_rank_num = td_list[0].text link_a = td_list[1].find(name="a") tv_url = root_url + link_a["href"] tv_name = link_a.text tv_category = td_list[2].text.strip() tv_status = td_list[3].text.strip()tv_update_day = td_list[4].text.strip() tv_return_date = td_list[5].text.strip()tv_item = TtmjTvPlayItem() tv_item.tv_play_name = tv_nametv_item.tv_play_name_en = tv_url.split("/")[-1].replace(".", " ")[:-5]tv_item.tv_play_name_en_dot = tv_url.split("/")[-1][:-5]tv_item.tv_play_name_ch = tv_name.split(" ")[0] tv_item.tv_play_rank = int(tv_rank_num) tv_item.tv_play_category = tv_categorytv_item.tv_play_state = tv_status tv_item.tv_play_update_day = tv_update_day tv_item.tv_play_return_date = tv_return_datetv_item.tv_play_url = tv_url tv_item.tv_play_cur_season = 0 tv_item_dict = dict( (name, getattr(tv_item, name)) for name in dir(tv_item) if not name.startswith('__')) print("complete Item: %s" % (tv_item.tv_play_name))result_dict.append(tv_item_dict)def main_grab(page_list): loop = asyncio.get_event_loop() # 获取事件循环 tasks = [grab_page(url) for url in page_list] # 把所有任务放到一个列表中loop.run_until_complete(asyncio.wait(tasks)) # 激活协程 loop.close() # 关闭事件循环def writeToDb(): for tv_item in result_dict:ttmj_collection.insert(tv_item) print("insert item: " + tv_item["tv_play_name"]) client.close()if __name__ == '__main__': start_url = "http://www.ttmeiju.vip/index.php/summary/index/p/**.html" page_list = generateRequestList(start_url, 1, 36) start = time.time()main_grab(page_list) print('爬取总耗时:%.5f秒' % float(time.time() - start))writeToDb() print('总耗时:%.5f秒' % float(time.time() - start))

aiohttp的关键写法,就是在开头,得声明信号量,这里皮爷申请的是 80 个。

接着就是 main_grab 方法中,开始调用 aiohttp。 aiohttp的方法,都需要以

async def

开头来定义,其中,需要等待的地方,可以用

await

来写。皮爷的这个代码,你完全可以照猫画虎的写出自己的逻辑。如果还有什么不懂的,自己百度或者谷歌搜索 aiohttp 就可以,网上例子一大堆,都很简单,看了也没啥用。还不如实际的撸个项目,加深体验。

我们来看结果,爬取35个网页总共用了 2 秒多:

你没看错,单纯的爬取网页,就 2 秒。

数据库中是:

插入数据库,皮爷是一条一条插入的,所以这个耗时很严重,导致整个工程运行了 35 秒:

从之前的 130 秒,到现在的 35 秒,你说速度是不是快了很多???你说快不快?是不是比刘翔还快??接下来快看骚操作怎么搞。

0x04 骚操作福利

骚操作,就要骚起来。你看皮爷用 aiohttp 写的Python运行起来是不是很给力?不但爬取数据,还能将数据结果存储到服务器里面。你有没有想过,这个代码是不是可以放到服务器上面让服务器自己跑???

答案当然是:可以的!!!

没错,你以后写的 py 文件,均可以放到服务器上面自动执行。不再需要像现在这样,自己写了代码,在ide里面跑一边之后,就荒废了。

那么问题来了,首先,你是不是得有个服务器啊?皮爷不亏待你们,特意给你们准备了优惠券,有没有的都可以来领取。

阿里云部分: 【阿里云新人1888元云产品通用代金券】: https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=nrkmbo9q【阿里云爆款云主机,2折优惠券】:https://promotion.aliyun.com/ntms/act/qwbk.html?userCode=nrkmbo9q【阿里云企业级服务器2折优惠券】:https://promotion.aliyun.com/ntms/act/enterprise-discount.html?userCode=nrkmbo9q腾讯云:【新客户无门槛领取总价值高达2775元代金券,每种代金券限量500张,先到先得】:https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=b351b2fc50b15866ff9d19b58a5df0f5&from=console【腾讯云服务器、云数据库特惠,3折优惠券】:https://cloud.tencent.com/redirect.php?redirect=1014&cps_key=b351b2fc50b15866ff9d19b58a5df0f5&from=console

有了服务器,那么将本地文件上传到服务器上面,只需要用

scp

命令就好:

$ scp <本地文件路径> <服务器角色>@<服务器ip地址>:<服务器文件路径>

上传代码参考文章

那么怎么定是执行呢?服务器一般都是 linux 系统,linux 系统自带一个命令叫

crontab

,用这个命令就可以定制执行了。

这一套组合拳打下来,你说骚不骚?

0x05 最后总结

爬虫用 aiohttp 来写还是用 Scrapy 来写,自己定夺,他们各有各的好处。

Scrapy框架完整,结果清晰;

aiohttp 速度更快,非常灵活。

所以,想用什么写爬虫,要根据你自己的需求来定。但是皮爷最近搞的东西,打算用 aiohttp 来自己做一套框架,来专门为自己使用。

Python爬虫加速神器的小试的更多相关文章

  1. Python爬虫之使用celery加速爬虫

      celery是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度.关于celery的更多介绍及例子,笔者可以参考文章Python之celery的简介与使用.   本文将介绍 ...

  2. python爬虫学习(6) —— 神器 Requests

    Requests 是使用 Apache2 Licensed 许可证的 HTTP 库.用 Python 编写,真正的为人类着想. Python 标准库中的 urllib2 模块提供了你所需要的大多数 H ...

  3. 小白学 Python 爬虫(4):前置准备(三)Docker基础入门

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  4. Python爬虫入门一之综述

    大家好哈,最近博主在学习Python,学习期间也遇到一些问题,获得了一些经验,在此将自己的学习系统地整理下来,如果大家有兴趣学习爬虫的话,可以将这些文章作为参考,也欢迎大家一共分享学习经验. Pyth ...

  5. python爬虫学习 —— 总目录

    开篇 作为一个C党,接触python之后学习了爬虫. 和AC算法题的快感类似,从网络上爬取各种数据也很有意思. 准备写一系列文章,整理一下学习历程,也给后来者提供一点便利. 我是目录 听说你叫爬虫 - ...

  6. 【图文详解】python爬虫实战——5分钟做个图片自动下载器

    python爬虫实战——图片自动下载器 之前介绍了那么多基本知识[Python爬虫]入门知识,(没看的先去看!!)大家也估计手痒了.想要实际做个小东西来看看,毕竟: talk is cheap sho ...

  7. 【Python爬虫】入门知识

    爬虫基本知识 这阵子需要用爬虫做点事情,于是系统的学习了一下python爬虫,觉得还挺有意思的,比我想象中的能干更多的事情,这里记录下学习的经历. 网上有关爬虫的资料特别多,写的都挺复杂的,我这里不打 ...

  8. Python实战:Python爬虫学习教程,获取电影排行榜

    Python应用现在如火如荼,应用范围很广.因其效率高开发迅速的优势,快速进入编程语言排行榜前几名.本系列文章致力于可以全面系统的介绍Python语言开发知识和相关知识总结.希望大家能够快速入门并学习 ...

  9. Python爬虫入门:综述

    大家好哈,最近博主在学习Python,学习期间也遇到一些问题,获得了一些经验,在此将自己的学习系统地整理下来,如果大家有兴趣学习爬虫的话,可以将这些文章作为参考,也欢迎大家一共分享学习经验. Pyth ...

随机推荐

  1. linux每日命令(5):mkdir命令

    linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录. 1.命令格式: mkdir [选项] 目录名或路径名 2. ...

  2. 【Android】Android设计准则

    准则 下面的这些设计准则是为了让Android的用户体验团队保持用户最佳的体验而发明设计的. 把他们融合到你的创造力中,作为你的设计理念,而不是有意地去使用. 吸引我 用惊奇的方式来取悦我 一个漂亮的 ...

  3. default listener is not configured in grid infrastructure home

    Oracle Restart enable database creation requries Default listener configured and running in Grid Inf ...

  4. 跨控制器跳转view——RedirectToRoute和RedirectToAction

    已知控制器AccountController.cs和HomeController.cs,如果从页面Account/Login直接跳转到Home/Index,可以利用RedirectToRoute和Re ...

  5. 【原】使用Json作为Python和C#混合编程时对象转换的中间文件

    一.Python中自定义类对象json字符串化的步骤[1]   1. 用 json 或者simplejson 就可以: 2.定义转换函数: 3. 定义类 4. 生成对象 5.dumps执行,引入转换函 ...

  6. net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting head

    使用docker 拉镜像的时候,出现下面的错误: net/http: request canceled while waiting for connection (Client.Timeout exc ...

  7. java代码执行字符串中的逻辑运算方法

    转载:https://www.jb51.net/article/143967.htm 方法一:Java调用js方法执行: /** * * @author: Longjun * @Description ...

  8. MTK 隐藏底部状态栏

    步骤: 源码/frameworks/base/policy/src/com/android/internal/policy/impl目录下修改PhoneWindowManager.java文件. 修改 ...

  9. Nginx-nginx和反向代理

    使用版本:nginx-1.10.2(windows环境使用稳定版本) 下载地址:http://nginx.org 什么是nginx? Nginx (engine x) 是一款轻量级的Web 服务器 . ...

  10. Git 学习笔记--拉取远程分支到本地

    1.查看远程分支,和上面的第一步相同2. 从远程获取最新版本到本地 git fetch origin master:temp git fetch origin master:temp 这句命令的意思是 ...