python爬虫入门(四)利用多线程爬虫

爬虫数据采集接单,大小不限,欢迎各位科研教师,学生,企业相关人员咨询,QQ:739848314



先回顾前面学过的一些知识

1.一个cpu一次只能执行一个任务,多个cpu同时可以执行多个任务
2.一个cpu一次只能执行一个进程,其它进程处于非运行状态
3.进程里包含的执行单元叫线程,一个进程可以包含多个线程
4.一个进程的内存空间是共享的,每个进程里的线程都可以使用这个共享空间
5.一个线程在使用这个共享空间的时候,其它的线程必须等待(阻塞状态)
6.互斥锁作用就是防止多个线程同时使用这块内存空间,先使用的线程会将空间上锁,其它的线程处于等待状态。等锁开了才能进
7.进程:表示程序的一次执行
8.线程:CPU运算的基本调度单位
9.GIL(全局锁):python里的执行通行证,而且只有一个。拿到通行证的线程就可以进入CPU执行任务。没有GIL的线程就不能执行任务
10.python的多线程适用于大量密集的I/O处理
11.python的多进程适用于大量的密集并行计算

多线程爬取糗事百科
复制代码
#!/usr/bin/env python

-- coding:utf-8 --

使用了线程库

import threading

队列

from Queue import Queue

解析库

from lxml import etree

请求处理

import requests

json处理

import json
import time

class ThreadCrawl(threading.Thread):
def init(self, threadName, pageQueue, dataQueue):
#threading.Thread.init(self)
# 调用父类初始化方法
super(ThreadCrawl, self).init()
# 线程名
self.threadName = threadName
# 页码队列
self.pageQueue = pageQueue
# 数据队列
self.dataQueue = dataQueue
# 请求报头
self.headers = {‘User-Agent’:‘Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36’}

def run(self):
    print "启动 " + self.threadName
    while not CRAWL_EXIT:
        try:
            # 取出一个数字,先进先出
            # 可选参数block,默认值为True
            #1. 如果对列为空,block为True的话,不会结束,会进入阻塞状态,直到队列有新的数据
            #2. 如果队列为空,block为False的话,就弹出一个Queue.empty()异常,
            page = self.pageQueue.get(False)
            url = "http://www.qiushibaike.com/8hr/page/" + str(page) +"/"
            #print url
            content = requests.get(url, headers = self.headers).text
            time.sleep(1)
            self.dataQueue.put(content)
            #print len(content)
        except:
            pass
    print "结束 " + self.threadName

class ThreadParse(threading.Thread):
def init(self, threadName, dataQueue, filename, lock):
super(ThreadParse, self).init()
# 线程名
self.threadName = threadName
# 数据队列
self.dataQueue = dataQueue
# 保存解析后数据的文件名
self.filename = filename
# 锁
self.lock = lock

def run(self):
    print "启动" + self.threadName
    while not PARSE_EXIT:
        try:
            html = self.dataQueue.get(False)
            self.parse(html)
        except:
            pass
    print "退出" + self.threadName

def parse(self, html):
    # 解析为HTML DOM
    html = etree.HTML(html)

    node_list = html.xpath('//div[contains(@id, "qiushi_tag")]')

    for node in node_list:
        # xpath返回的列表,这个列表就这一个参数,用索引方式取出来,用户名
        username = node.xpath('./div/a/@title')[0]
        # 图片连接
        image = node.xpath('.//div[@class="thumb"]//@src')#[0]
        # 取出标签下的内容,段子内容
        content = node.xpath('.//div[@class="content"]/span')[0].text
        # 取出标签里包含的内容,点赞
        zan = node.xpath('.//i')[0].text
        # 评论
        comments = node.xpath('.//i')[1].text

        items = {
            "username" : username,
            "image" : image,
            "content" : content,
            "zan" : zan,
            "comments" : comments
        }

        # with 后面有两个必须执行的操作:__enter__ 和 _exit__
        # 不管里面的操作结果如何,都会执行打开、关闭
        # 打开锁、处理内容、释放锁
        with self.lock:
            # 写入存储的解析后的数据
            self.filename.write(json.dumps(items, ensure_ascii = False).encode("utf-8") + "\n")

CRAWL_EXIT = False
PARSE_EXIT = False

def main():
# 页码的队列,表示20个页面
pageQueue = Queue(20)
# 放入1~10的数字,先进先出
for i in range(1, 21):
pageQueue.put(i)

# 采集结果(每页的HTML源码)的数据队列,参数为空表示不限制
dataQueue = Queue()

filename = open("duanzi.json", "a")
# 创建锁
lock = threading.Lock()

# 三个采集线程的名字
crawlList = ["采集线程1号", "采集线程2号", "采集线程3号"]
# 存储三个采集线程的列表集合
threadcrawl = []
for threadName in crawlList:
    thread = ThreadCrawl(threadName, pageQueue, dataQueue)
    thread.start()
    threadcrawl.append(thread)


# 三个解析线程的名字
parseList = ["解析线程1号","解析线程2号","解析线程3号"]
# 存储三个解析线程
threadparse = []
for threadName in parseList:
    thread = ThreadParse(threadName, dataQueue, filename, lock)
    thread.start()
    threadparse.append(thread)

# 等待pageQueue队列为空,也就是等待之前的操作执行完毕
while not pageQueue.empty():
    pass

# 如果pageQueue为空,采集线程退出循环
global CRAWL_EXIT
CRAWL_EXIT = True

print "pageQueue为空"

for thread in threadcrawl:
    thread.join()
    print "1"

while not dataQueue.empty():
    pass

global PARSE_EXIT
PARSE_EXIT = True

for thread in threadparse:
    thread.join()
    print "2"

with lock:
    # 关闭文件
    filename.close()
print "谢谢使用!"

if name == “main”:
main()

你可能感兴趣的:(python爬虫入门(四)利用多线程爬虫)