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

本文共 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

你可能感兴趣的文章
基于Qt的OpenGL可编程管线学习(9)- X射线
查看>>
qt 元对象
查看>>
onApp可以为云主机做些什么?
查看>>
PL*SQL 常用命令
查看>>
Linux install Mysql server
查看>>
Tomcat Too Many Open Files
查看>>
varnish
查看>>
CentOS 7 笔记
查看>>
MySQL CSV file
查看>>
文件权限管理的扩展
查看>>
数据结构的研究对象
查看>>
EXTJS 4.0 核心代码分析 (一)
查看>>
如何让路由器使用起来更加便捷
查看>>
实现自动为用户映射网络驱动器
查看>>
Kali 2/3中启动带数据库支持的MSF
查看>>
java调用新浪微博API发布第一条微博
查看>>
django实用技巧:template模板的使用
查看>>
python正则表达式基础
查看>>
Git Flow
查看>>
Objective-C --- - UICollectionView (梳理总结)
查看>>