博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程爬虫实现(下)
阅读量:4692 次
发布时间:2019-06-09

本文共 3042 字,大约阅读时间需要 10 分钟。

本文首发于

本文是的续篇,实现基于多线程的 翻页、抓取二级页面。使用豆瓣top250作为例子,为了防止请求过快ip被封,我们每页只抓取5个电影。

爬虫代码如下

import requestsimport timefrom threading import Threadfrom queue import Queueimport jsonfrom bs4 import BeautifulSoupdef run_time(func):    def wrapper(*args, **kw):        start = time.time()        func(*args, **kw)        end = time.time()        print('running', end-start, 's')    return wrapperclass Spider():    def __init__(self):        self.start_url = 'https://movie.douban.com/top250'        self.qurl = Queue()        self.data = list()        self.item_num = 5 # 限制每页提取个数(也决定了二级页面数量)防止对网页请求过多        self.thread_num = 10 # 抓取二级页面线程数量        self.first_running = True    def parse_first(self, url):        print('crawling', url)        r = requests.get(url)        soup = BeautifulSoup(r.content, 'lxml')        movies = soup.find_all('div', class_ = 'info')[:self.item_num]        for movie in movies:            url = movie.find('div', class_ = 'hd').a['href']            self.qurl.put(url)        nextpage = soup.find('span', class_ = 'next').a        if nextpage:            nexturl = self.start_url + nextpage['href']            self.parse_first(nexturl)        else:            self.first_running = False    def parse_second(self):        while self.first_running or not self.qurl.empty():            url = self.qurl.get()            print('crawling', url)            r = requests.get(url)            soup = BeautifulSoup(r.content, 'lxml')            mydict = {}            title = soup.find('span', property = 'v:itemreviewed')            mydict['title'] = title.text if title else None            duration = soup.find('span', property = 'v:runtime')            mydict['duration'] = duration.text if duration else None            time = soup.find('span', property = 'v:initialReleaseDate')            mydict['time'] = time.text if time else None            self.data.append(mydict)    @run_time    def run(self):        ths = []        th1 = Thread(target=self.parse_first, args=(self.start_url, ))        th1.start()        ths.append(th1)        for _ in range(self.thread_num):            th = Thread(target=self.parse_second)            th.start()            ths.append(th)        for th in ths:            th.join()        s = json.dumps(self.data, ensure_ascii=False, indent=4)        with open('top_th1.json', 'w', encoding='utf-8') as f:            f.write(s)        print('Data crawling is finished.')if __name__ == '__main__':    Spider().run()复制代码

这里的整体思路和上一篇文章没有什么区别。分配两个队列,一个存储二级页面的URL,一个存储抓取到的数据。一级页面单独开一个线程,将二级页面URL不断填入队列中。解析二级页面URL时开启多个线程提高抓取速度。

除此之外,还需要说明一个地方

我们上一篇文章中,因为URL队列是事先产生的,而不是生产和消耗URL程序同时进行,因此队列一旦为空即结束爬虫。而这里的一级页面和二级页面的解析是同时进行的,也就是说二级页面URL是边生产边消耗的,这时我们就要保证

  • 所有页面解析结束可以退出所有线程(如果只是单纯while True,URL列表为空时,消耗线程就会永远等下去)
  • 不会因为二级页面URL消耗太快而使队列提前为空,提早退出爬虫

对于第二点,这里定义了self.first_running,它如果是True,则表示一级页面还没运行完成。此时即使二级页面的URL队列已经空了,也要继续等待一级页面解析后产生新的二级页面URL。

另外,由于这次URL队列是典型的,因此如果不想自己实现Condition锁的话,就用Queue来代替list。

读者也可以试着更改self.thread_num看爬虫速度有什么改变。

欢迎关注我的知乎专栏

专栏主页:

专栏目录:

版本说明:

转载于:https://juejin.im/post/5b129b7d5188257d4f0d793b

你可能感兴趣的文章
python入门之正则表达式
查看>>
SAS学习经验总结分享:篇五-过程步的应用
查看>>
Android创建文件夹及文件并写入数据
查看>>
file的getPath getAbsolutePath和getCanonicalPath的不同
查看>>
课时4—切入切出动画
查看>>
eclipse 编辑 python 中文乱码的解决方案
查看>>
Python 爬虫的集中简单方式
查看>>
数据库MySQL/mariadb知识点——触发器
查看>>
Ubuntu做Tomcat服务:insserv: warning: script 'tomcat' missing LSB tags and overrides
查看>>
Binary Agents
查看>>
入门Webpack,看这篇就够了
查看>>
短信拦截马”黑色产业链与溯源取证研究
查看>>
Mac Xdebug安装时遇到了Zend Engine API 不一致的问题
查看>>
最小公倍数
查看>>
asp.net如何定时执行任务
查看>>
在github上实现页面托管预览功能
查看>>
css选择器
查看>>
prim
查看>>
给陌生人写一封信
查看>>
noip2013花匠
查看>>