python:多线程爬取糗事百科

多线程爬取糗事百科

# -*- coding:utf-8 -*-

#使用了线程库
import threading

#请求处理
import requests

#队列
from Queue import Queue

#解析库
from lxml import etree

#json处理
import json
import time


#标志位
CRAWL_EXIT = False
PARSE_EXIT = False

#threading.Thread是继承
class ThreadCrawl(threading.Thread):
    #初始化方法
    def __int__(self , threadName , pageQueue , dataQueue):

        #调用父类初始化方法
        super(ThreadCrawl , self).__init__()

        #线程名
        self.threadName = threadName

        #页码队列
        self.pageQueue = pageQueue

        #数据队列
        self.dataQueue = dataQueue

        #请求报头
        self.headers = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}

    def run(self):
        print "启动" + self.threadName

        while not CRAWL_EXIT:
            try:
                #取出一个数字,先进先出
                #1.如果队列为空,block为True的话,不会结束,会进入阻塞状态,直到队列有新的数据
                #2.如果队列为空,block为False的话,就会弹出一个Queue.empty()异常
                #block的默认值为True

                #拿到一个页数
                page = self.pageQueue.get(False)

                url = "http://www.qiushibaike.com/8hr/page/" + str(page) +"/"

                #获取页面内容
                content = request.gets(url , headers = self.headers).text
                time.sleep(1)
                #把获取到的内容放入队列
                self.dataQueue.put(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源码
                html = self.dataQueue.get(False)
                self.parse(html)

            except:
                pass

        print "退出" + self.threadName

    #解析html的方法,提取我们需要的数据
    def parse(self , 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") )


def main():
    # 页码的队列,表示20个页面
    pageQueue = Queue(20)
    #向pageQueue中放入页码
    for i in range(1 , 21):
        pageQueue.put(i)

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

    #段子写入的文件
    filename = open("duanzi.json" , "a")

    #创建锁
    lock = threading.Lock()

    #三个采集线程的名字
    crawList = ["采集线程1号" , "采集线程2号" , "采集线程3号"]

    #存储三个采集线程的列表集合
    threadcrawl = []
    for threadName in crawlList:
        #创建采集线程对象
        thread = ThreadCrawl(threadName , pageQueue , dataQueue)
        #开启线程
        thread.start()
        #将线程对象添加到线程集合中去
        threadcrawl.append(thread)

    #三个解析线程的名字
    parseList = ["解析线程1号" , "解析线程2号" , "解析线程3号"]

    #存储3个解析线程
    threadparse = []

    for threadName int 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()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值