欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

爬虫之多线程

程序员文章站 2022-03-09 09:26:06
...

1.引入

之前写的爬虫都是单个线程的,一旦某个地方卡住不动了,那就要演员等待下去了,所以我们可以使用多线程或多进程来处理

但是我个人不建议用,不过 还是简单的介绍下

2.使用

爬虫使用多线程来处理网络请求,使用线程来处理URL队列中的url,然后将url返回的结果保存在另一个队列中,其它线程在读取这个队列中的数据,然后写到文件中去

3. 主要组成部分

3.1 URL队列和结果队列

将要爬取的url放在一个队列中,这里使用标准库Queue,访问url后的结果保存在结果队列中

初始化一个URL队列

from queue import Queue
url_queue=Queue()
html_queue=Queue()

3.2 请求线程

使用多个线程,不停的取URL队列中的url,并进行处理:

from threading import Thread
class ThreadInfo(Thread):
    def __init__(self,url_queue,html_queue):
        Thread.__init__(self)
        self.url_queue=url_queue
        self.html_queue=html_queue
    def run(self):
        user_agents = [
            "User-Agent:Mozilla/5.0(Windows;U;WindowsNT6.1;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50",
            "User-Agent:Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
        ]
        headers = {
            "User-Agent": choice(user_agents)
        }
        while self.url_queue.empty()==False:
            url=self.url_queue.get()
            response = requests.get(url,headers=headers)
            if response.status_code==200:
                self.html_queue.put(response.text)

如果队列为空,线程就会被阻塞,知道队列不为空,处理队列中的一条数据后,就需要通知队列已经这条数据处理完

3.3 处理线程

处理结果队列中的数据,并保存到文件中,如果使用多个线程的话必须要给文件加上锁

lock=threading.Lock()
f=codecs.open('xiaohua.txt','w','utf-8')

当线程需要写入文件的时候,可以这样处理:

with lock:
	f.write(something)

4. 一个小例子

这里举一个爬取糗事百科的段子的例子,

from threading import Thread
from queue import Queue
from lxml import etree
from random import choice
import requests

#爬虫类
class CrawlInfo(Thread):
    def __init__(self,url_queue,html_queue):
        Thread.__init__(self)
        self.url_queue=url_queue
        self.html_queue=html_queue
    def run(self):
        user_agents = [
            "User-Agent:Mozilla/5.0(Windows;U;WindowsNT6.1;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50",
            "User-Agent:Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
        ]
        headers = {
            "User-Agent": choice(user_agents)
        }
        while self.url_queue.empty()==False:#url队列不为空的时候
            url=self.url_queue.get()
            response = requests.get(url,headers=headers)
            if response.status_code==200:
                self.html_queue.put(response.text)

#解析类
class ParseInfo(Thread):
    def __init__(self,html_queue):
        Thread.__init__(self)
        self.html_queue=html_queue
    def run(self):
        while self.html_queue.empty()==False:
            e = etree.HTML(self.html_queue.get())
            span_list=e.xpath('//div[@class="content"]/span[1]')
            with open('xiaohua.txt','a',encoding='utf-8') as f:
                for span in span_list:
                    info=span.xpath('string(.)')
                    f.write(info+'\n')
if __name__=='__main__':
    url_queue=Queue()#用来存储url的容器
    base_url="https://www.qiushibaike.com/text/page/{}/"
    html_queue=Queue()#用来存储爬取到的整个页面的html,还未解析
    for i in range(1,14):
        new_url=base_url.format(i)
        print(new_url)
        url_queue.put(new_url)

    Crawl_list=[]#用来放爬虫类的线程,因为下面要让3个线程都等待,所以需要存起来
    for i in range(0,3):#创建3个线程
        Crawl1=CrawlInfo(url_queue,html_queue)
        Crawl_list.append(Crawl1)
        Crawl1.start()

    for crawl in Crawl_list:
        crawl.join()

    parse_list=[]
    for i in range(0,3):

        parse=ParseInfo(html_queue)
        parse_list.append(parse)
        parse.start()
    for parse in parse_list:
        parse.join()