精华内容
下载资源
问答
  • 首先先了解多线程队列,生产消费模式的大致步骤。 1.主线程生成目标链接。 2.主线程开启子线程访问队列并爬取数据保存。 3.待队列目标为空时关闭线程。 示例代码 主要字段: city={ '河北省':['石家庄','保定市','...

    在爬取大量数据时,由于有成千上万的数据,单线程爬虫显然不能满足我们的需求,这时候多线程爬虫就来了,本篇文章使用Threading和Queue简单介绍。

    首先先了解多线程队列,生产消费模式的大致步骤。
    1.主线程生成目标链接。
    2.主线程开启子线程访问队列并爬取数据保存。
    3.待队列目标为空时关闭线程。

    示例代码

    主要字段:

    city={
        '河北省':['石家庄','保定市','秦皇岛','唐山市','邯郸市','邢台市','沧州市','承德市','廊坊市','衡水市','张家口'],
       '山西省':['太原市','大同市','阳泉市','长治市','临汾市','晋中市','运城市','晋城市','忻州市','朔州市','吕梁市'],
        '内蒙古':['呼和浩特','呼伦贝尔','包头市','赤峰市','乌海市','通辽市','鄂尔多斯','乌兰察布','巴彦淖尔'],
       '辽宁省':['盘锦市','鞍山市','抚顺市','本溪市','铁岭市','锦州市','丹东市','辽阳市','葫芦岛','阜新市','朝阳市','营口市'],
       '吉林省':['吉林市','通化市','白城市','四平市','辽源市','松原市','白山市'],
       '黑龙江省':['伊春市','牡丹江','大庆市','鸡西市','鹤岗市','绥化市','双鸭山','七台河','佳木斯','黑河市','齐齐哈尔市'],
       '江苏省':['无锡市','常州市','扬州市','徐州市','苏州市','连云港','盐城市','淮安市','宿迁市','镇江市','南通市','泰州市'],
       '浙江省':['绍兴市','温州市','湖州市','嘉兴市','台州市','金华市','舟山市','衢州市','丽水市'],
       '安徽省':['合肥市','芜湖市','亳州市','马鞍山','池州市','淮南市','淮北市','蚌埠市','巢湖市','安庆市','宿州市','宣城市','滁州市','黄山市','六安市','阜阳市','铜陵市'],
       '福建省':['福州市','泉州市','漳州市','南平市','三明市','龙岩市','莆田市','宁德市'],
       '江西省':['南昌市','赣州市','景德镇','九江市','萍乡市','新余市','抚州市','宜春市','上饶市','鹰潭市','吉安市'],
       '山东省':['潍坊市','淄博市','威海市','枣庄市','泰安市','临沂市','东营市','济宁市','烟台市','菏泽市','日照市','德州市','聊城市','滨州市','莱芜市'],
       '河南省':['郑州市','洛阳市','焦作市','商丘市','信阳市','新乡市','安阳市','开封市','漯河市','南阳市','鹤壁市','平顶山','濮阳市','许昌市','周口市','三门峡','驻马店'],
       '湖北省':['荆门市','咸宁市','襄樊市','荆州市','黄石市','宜昌市','随州市','鄂州市','孝感市','黄冈市','十堰市'],
       '湖南省':['长沙市','郴州市','娄底市','衡阳市','株洲市','湘潭市','岳阳市','常德市','邵阳市','益阳市','永州市','张家界','怀化市'],
       '广东省':['江门市','佛山市','汕头市','湛江市','韶关市','中山市','珠海市','茂名市','肇庆市','阳江市','惠州市','潮州市','揭阳市','清远市','河源市','东莞市','汕尾市','云浮市'],
       '广西省':['南宁市','贺州市','柳州市','桂林市','梧州市','北海市','玉林市','钦州市','百色市','防城港','贵港市','河池市','崇左市','来宾市'],
       '海南省':['海口市','三亚市'],
       '四川省':['乐山市','雅安市','广安市','南充市','自贡市','泸州市','内江市','宜宾市','广元市','达州市','资阳市','绵阳市','眉山市','巴中市','攀枝花','遂宁市','德阳市'],
       '贵州省':['贵阳市','安顺市','遵义市','六盘水'],
       '云南省':['昆明市','玉溪市','大理市','曲靖市','昭通市','保山市','丽江市','临沧市'],
       '西藏':['拉萨市','阿里'],
       '陕西省':['咸阳市','榆林市','宝鸡市','铜川市','渭南市','汉中市','安康市','商洛市','延安市'],
       '甘肃省':['兰州市','白银市','武威市','金昌市','平凉市','张掖市','嘉峪关','酒泉市','庆阳市','定西市','陇南市','天水市'],
       '青海省':['西宁市'],
       '银川省':['银川市','固原市','青铜峡市','石嘴山市','中卫市']
      }
    years=['2011','2012','2013','2014','2015','2016','2017','2018','2019','2020']
    month=['01','02','03','04','05','06','07','08','09','10','11','12']
    month_less=['01','02','03','04','05','06','07','08','09','10']
    title=['日期','最高气温','最低气温','天气','风向']
    

    导入所需要的包:

    from lxml import etree
    import csv
    import time
    import bs4
    import random
    import requests
    from xpinyin import Pinyin
    import os
    import threading
    from queue import Queue
    

    创建队列

    q=Queue()
    

    因为爬取的是全国省市2011年至今的天气数据,所以本段代码创建了省会路径并想队列传递目标链接。

    for province in city.keys():
        path='./Thread_Test/{}'.format(province)
        if os.path.exists(path):
            pass
        else:
            os.mkdir(path)
        for nano_city in city[province]:
            path='./Thread_Test/{}/{}'.format(province,nano_city)
            if os.path.exists(path):
                pass
            else:
                os.mkdir(path)
            p=Pinyin()
            if '市' in nano_city:
                str_city=nano_city.split('市')[0]
                str_city=p.get_pinyin(u'{}'.format(str_city),'')
                print(str_city)
            else:
                str_city=p.get_pinyin(u'{}'.format(nano_city),'')
            for y in years:
                path='./Thread_Test/{}/{}/{}'.format(province,nano_city,y)
                if os.path.exists(path):
                    pass
                else:
                    os.mkdir(path)
                if y=='2020':
                    for m in month_less:
                        url='https://lishi.tianqi.com/{}/{}.html'.format(str_city,y+m)
                        info=[province,nano_city,y,m]
                        q.put([url,info])
                else:
                    for m in month:
                        url='https://lishi.tianqi.com/{}/{}.html'.format(str_city,y+m)
                        info=[province,nano_city,y,m]
                        q.put([url,info])
    print(q.qsize())
    

    队列中共有31388条链接

    创建线程任务方法:

    def working():
        while True: #需要使用while 否则线程执行完一次操作就关闭了
            url = q.get() #默认队列为空时,线程暂停
            doing(url)
            q.task_done()#告诉队列本次取操作已经完毕
    
    def doing(url):  #线程在获取到链接后的行为
        rsp=requests.get(url=url[0],headers=headers)
        html_4=bs4.BeautifulSoup(rsp.text,'html.parser')
        ul=html_4.find('ul',class_='thrui')
        f=open('./Thread_Test/{}/{}/{}/{}.csv'.format(url[1][0],url[1][1],url[1][2],url[1][3]),'w',encoding='utf-8',newline='')
        csv_writer=csv.writer(f)
        csv_writer.writerow(title)
        for i in ul.find_all('li'):
            lis=[]
            a=i.find_all('div')
            for j in a:
                if len(j.text.split())==0:
                    pass
                else:
                    lis.append(j.text.split()[0])
            print(url[1][0],url[1][1],url[1][2],url[1][3],'剩余',q.qsize())
            csv_writer.writerow(lis)
        f.close()
    

    创建子线程

    threads = []
    for i in range(10): #开启十个子线程
        t = threading.Thread(target=working) #线程的目标任务为working方法
        threads.append(t)
    

    开启子线程

    for item in threads:
        item.setDaemon(True)
        item.start()
    q.join()  #在队列为空时才进行后面的语句,需要配合task_done()使用
    

    基本的多线程爬虫就完成了。

    还有一个更优的方法
    生成一个新的队列来储存主线程创建的十个子线程获取到网页数据,子线程并不直接写入,而是交给主线程,主线程再生成新的子线程来写入队列里的数据,这样就减少了十个子线程等待写入文件的时间,专注于爬取数据。

    由于数据量太大,测试仅选择了一个省的数据进行爬取。约1300条数据在这里插入图片描述
    此图为线程直接写入所需时间

    在这里插入图片描述
    此图为使用新线程专门处理写入数据所需时间

    在这里插入图片描述
    次图为单线程爬虫所需时间

    可以看到使用新线程专门处理写入数据时,速度比边爬边写快了30秒=百分之二十,而单线程花费了恐怖的1405秒,为多线程的十倍之多,可见多线程在爬取大量数据时是非常有用的。
    当然,这么快的速度肯定会遭到反扒处理,封ip之类的,所以要事先准备一个ip池,每个线程在爬取一段时间后就更换一个ip,线程不建议开太多,避免给目标网站服务器造成太大压力,做一个绅士爬虫!

    这段代码还可以再继续优化:
    1.爬虫的类库提取
    2.线程的类库提取
    3.存数据库的类库提取
    4.main()函数优化

    初上手多线程,对于锁什么的还不是很懂,若有错误的地方欢迎指明。

    展开全文
  • Python+多线程+队列爬虫

    千次阅读 2017-03-08 16:04:22
    Python+多线程+队列爬虫例子

    Python+多线程+队列,爬虫例子

    # -*- coding: utf-8-*-
    import urllib2
    import urllib
    import json
    import time
    import datetime
    import threading
    import Queue
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    
    def get_response(url):
        for a in range(3):
            try:
                request = urllib2.Request(url)
                response = urllib2.urlopen(request)
                result= response.read()
    
                return result
    
            except Exception,e:
                print e
                time.sleep(2)
                continue
    
    class ThreadCity(threading.Thread):
        def __init__(self,queue_zq_citys):
            threading.Thread.__init__(self)
            self.queue_zq_citys=queue_zq_citys
        def run(self):
    
            sql = 'select cityid,cname from  table '
            citylist=DBHelper.SqlHelper.ms.ExecQuery(sql)
    
            for c in citylist:
                try:
                    #根据搜索城市名称获取城市
                    qm_cname=c[1]
                    data=urllib.urlencode({'q':qm_cname})
                    url='xxx.xxx.com/ajax.do?' % data
                    result=get_response(url)
                    cjson=json.loads(result.decode('gb2312', 'ignore')) #json格式字符串转换为python对象
                    cityId=cjson["id"]
                    cityname=cjson["cName"]
    
                    #加入队列
                    self.queue_zq_citys.put({'cityid':cityId,'cityname':cityname})
                    time.sleep(1)
    
                except Exception,e:
                   pass
    
    class ThreadCityDB(threading.Thread):
        def __init__(self, queue_zq_citys):
            threading.Thread.__init__(self)
            self.queue_zq_citys = queue_zq_citys
        def run(self):
            while True:
                try:
                    if self.queue_zq_citys.empty(): #队列为空
                        pass
                    else:
                        citys=self.queue_zq_citys.get() #从队列中取出数据
                        if  citys is not None:
                            sql = "insert into Table(cityid,cityname) values(%s,'%s')" % (
                                citys['cityid'], citys['cityname'])
                            #print  sql
                            DBHelper.SqlHelper.ms.ExecNonQuery(sql.encode('utf-8'))
                            self.queue_zq_citys.task_done() #告诉线程我完成了这个任务 是否继续join阻塞 让线程向前执行或者退出
                        else:
                            pass
                except Exception,e:
                    pass
    
    
    
    def main():
        try:
    
            queue_zq_citys=Queue.Queue()  # 实例化存放抓取到的城市队列
    
            #创建线程
            city=ThreadCity(queue_zq_citys) #抓取线程 入队操作
            cityDB=ThreadCityDB(queue_zq_citys) #出队操作 存入数据库
    
            #启动线程
            city.start()
            cityDB.start()
    
            #阻塞等待子线程执行完毕后再执行主线程
            city.join()
            cityDB.join()
        except Exception,e:
            pass
    
    if __name__ == '__main__':
        main()


    展开全文
  • 题记:作为测试工程师经常需要解决测试数据来源的问题,解决思路无非是三种:(1)直接从生产环境拷贝...作为一个精益求精的测试工程师,决定研究一下多线程爬虫领域的应用,以提高爬虫的效率。一、为什么需要多线...

    题记:作为测试工程师经常需要解决测试数据来源的问题,解决思路无非是三种:(1)直接从生产环境拷贝真实数据 (2)从互联网上爬取数据 (3)自己用脚本或者工具造数据。前段时间,为了获取更多的测试数据,笔者就做了一个从互联网上爬取数据的爬虫程序,虽然功能上基本满足项目的需求,但是爬取的效率还是不太高。作为一个精益求精的测试工程师,决定研究一下多线程在爬虫领域的应用,以提高爬虫的效率。

    一、为什么需要多线程

    凡事知其然也要知其所以然。在了解多线程的相关知识之前,我们先来看看为什么需要多线程。打个比方吧,你要搬家了,单线程就类似于请了一个搬家工人,他一个人负责打包、搬运、开车、卸货等一系列操作流程,这个工作效率可想而知是很慢的;而多线程就相当于请了四个搬家工人,甲打包完交给已搬运到车上,然后丙开车送往目的地,最后由丁来卸货。

    由此可见多线程的好处就是高效、可以充分利用资源,坏处就是各个线程之间要相互协调,否则容易乱套(类似于一个和尚挑水喝、两个和尚抬水喝、三个和尚没水喝的窘境)。所以为了提高爬虫效率,我们在使用多线程时要格外注意多线程的管理问题。

    二、多线程的基本知识

    进程:由程序,数据集,进程控制块三部分组成,它是程序在数据集上的一次运行过程。如果同一段程序在某个数据集上运行了两次,那就是开启了两个进程。进程是资源管理的基本单位。在操作系统中,每个进程有一个地址空间,而且默认就有一个控制进程。

    线程:是进程的一个实体,是CPU调度和分派的基本单位,也是最小的执行单位。它的出现降低了上下文切换的消耗,提高了系统的并发性,并克服了一个进程只能干一件事的缺陷。线程由进程来管理,多个线程共享父进程的资源空间。

    进程和线程的关系:

    一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

    资源分配给进程,同一进程的所有线程共享该进程的所有资源。

    CPU分给线程,即真正在CPU上运行的是线程。

    线程的工作方式:

    如下图所示,串行指线程一个个地在CPU上执行;并行是在多个CPU上运行多个线程;而并发是一种“伪并行”,一个CPU同一时刻只能执行一个任务,把CPU的时间分片,一个线程只占用一个很短的时间片,然后各个线程轮流,由于时间片很短所以在用户看来所有线程都是“同时”的。并发也是大多数单CPU多线程的实际运行方式。

    1441963-20191109103614314-611376286.jpg

    进程的工作状态:

    一个进程有三种状态:运行、阻塞、就绪。三种状态之间的转换关系如下图所示:运行态的进程可能由于等待输入而主动进入阻塞状态,也可能由于调度程序选择其他进程而被动进入就绪状态(一般是分给它的CPU时间到了);阻塞状态的进程由于等到了有效的输入而进入就绪状态;就绪状态的进程因为调度程序再次选择了它而再次进入运行状态。

    1441963-20191109103630892-436380471.jpg

    三、多线程通信实例

    还是回到爬虫的问题上来,我们知道爬取博客文章的时候都是先爬取列表页,然后根据列表页的爬取结果再来爬取文章详情内容。而且列表页的爬取速度肯定要比详情页的爬取速度快。

    这样的话,我们可以设计线程A负责爬取文章列表页,线程B、线程C、线程D负责爬取文章详情。A将列表URL结果放到一个类似全局变量的结构里,线程B、C、D从这个结构里取结果。

    在PYTHON中,有两个支持多线程的模块:threading模块--负责线程的创建、开启等操作;queque模块--负责维护那个类似于全局变量的结构。这里还要补充一点:也许有同学会问直接用一个全局变量不就可以了么?干嘛非要用queue?因为全局变量并不是线程安全的,比如说全局变量里(列表类型)只有一个url了,线程B判断了一下全局变量非空,在还没有取出该url之前,cpu把时间片给了线程C,线程C将最后一个url取走了,这时cpu时间片又轮到了B,B就会因为在一个空的列表里取数据而报错。而queue模块实现了多生产者、多消费者队列,在放值取值时是线程安全的。

    废话不多说了,直接上代码给大伙看看:

    import threading #导入threading模块

    from queue import Queue #导入queue模块

    import time #导入time模块

    #爬取文章详情页

    defget_detail_html(detail_url_list, id):whileTrue:

    url= detail_url_list.get() #Queue队列的get方法用于从队列中提取元素

    time.sleep(2) #延时2s,模拟网络请求和爬取文章详情的过程

    print("thread {id}: get {url} detail finished".format(id=id,url=url)) #打印线程id和被爬取了文章内容的url

    #爬取文章列表页

    defget_detail_url(queue):for i in range(10000):

    time.sleep(1) #延时1s,模拟比爬取文章详情要快

    queue.put("http://testedu.com/{id}".format(id=i))#Queue队列的put方法用于向Queue队列中放置元素,由于Queue是先进先出队列,所以先被Put的URL也就会被先get出来。

    print("get detail url {id} end".format(id=i))#打印出得到了哪些文章的url

    #主函数

    if __name__ == "__main__":

    detail_url_queue= Queue(maxsize=1000) #用Queue构造一个大小为1000的线程安全的先进先出队列

    #先创造四个线程

    thread = threading.Thread(target=get_detail_url, args=(detail_url_queue,)) #A线程负责抓取列表url

    html_thread=[]for i in range(3):

    thread2= threading.Thread(target=get_detail_html, args=(detail_url_queue,i))

    html_thread.append(thread2)#B C D 线程抓取文章详情

    start_time =time.time()#启动四个线程

    thread.start()for i in range(3):

    html_thread[i].start()#等待所有线程结束,thread.join()函数代表子线程完成之前,其父进程一直处于阻塞状态。

    thread.join()for i in range(3):

    html_thread[i].join()print("last time: {} s".format(time.time()-start_time))#等ABCD四个线程都结束后,在主进程中计算总爬取时间。

    运行结果:

    1441963-20191109103649619-835647872.jpg

    后记:从运行结果可以看出各个线程之间井然有序地工作着,没有出现任何报错和告警的情况。可见使用Queue队列实现多线程间的通信比直接使用全局变量要安全很多。而且使用多线程比不使用多线程的话,爬取时间上也要少很多,在提高了爬虫效率的同时也兼顾了线程的安全,可以说在爬取测试数据的过程中是非常实用的一种方式。希望小伙伴们能够GET到哦!

    转自:https://mp.weixin.qq.com/s/LsRNxAVJywKwEXxo8WuwLw

    ---------------------------------------------------------------------------------

    关注微信公众号即可在手机上查阅,并可接收更多测试分享~

    1441963-20200419182634999-600106893.png

    展开全文
  • 编辑推荐:本文主要介绍对Python多线程爬虫实战的整体的思路,希望对大家有帮助。本文来知乎,由火龙果软件Alice编辑,推荐。最近在写爬虫程序爬取亚马逊上的评论信息,因此也自学了很多爬虫相关的知识,其实网络上...

    编辑推荐:

    本文主要介绍对Python多线程爬虫实战的整体的思路,希望对大家有帮助。

    本文来知乎,由火龙果软件Alice编辑,推荐。

    最近在写爬虫程序爬取亚马逊上的评论信息,因此也自学了很多爬虫相关的知识,其实网络上已经有很多基于Python的入门爬虫程序了,所以学习起来比较方便,唯独那个多线程爬虫一直都学的不是很明白,所以就写下这篇blog记录一下学到的一些东西(主要是对自己所学的一些东西进行整理和总结)。

    一、Python的多线程

    Python多线程网上的介绍很多了,但是一直都听说Python的多线程很鸡肋,为什么呢?为什么有人说

    Python 的多线程是鸡肋呢?里面的多位大佬已经做出了解释,其实就是因为Python多线程用到了全局解释器锁(GIL锁),这里引用一位大佬的回答

    Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,但同一时间只有一个线程在解释器中运行。

    对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。

    1.设置GIL。

    2.切换到一个线程去执行。

    3.运行。

    4.把线程设置为睡眠状态。

    5.解锁GIL。

    6.再次重复以上步骤。

    对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果某线程并未使用很多I/O操作,它会在自己的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。

    二、线程安全的队列 Queue

    队列这种东西大家应该都知道,就是一个先进先出的数据结构,而Python的标准库中提供了一个线程安全的队列,也就是说该模块是适用于多线程编程的先进先出(first-in,first-out,FIFO)数据结构,可以用来在生产者消费者线程之间安全地传递消息或其他数据。它会为调用者处理锁定,使用多个线程可以安全地处理同一个

    Queue 实例。Queue 的大小(其中包含的元素个数)可能要受限,以限制内存使用或处理。

    在Python 3中要引入Queue和Python 2中引入Queue是不同,引入方式如下:

    #python 2

    import Queue

    # python 3

    from queue import Queue

    因为是线程安全的,很自然就可以利用Queue来实现一个多线程爬虫咯,而Queue的一些常见操作如下:

    # 实例化一个队列,可以在指定队列大小

    q = Queue.Queue()

    q_50 = Queue.Queue(50) # 指定一个长度为50的队列

    # 入队一个数据data

    q.put(data)

    # 出队并赋值给item

    item = q.get()

    # 判断队列是否为空,是否满

    if q.empty():

    print('队列为空')

    if q.full():

    print('队列满')

    除了普通队列,标准库中还有优先队列和后进先出队列这两个队列,分别为LifoQueue和PriorityQueue,其引用方式与Queue类似,使用方法参考

    [Python标准库]Queue――线程安全的 FIFO 实现

    三、基于多线程爬虫爬取糗事百科的段子

    下面进入实战的一个代码,代码的理解也相对简单,相信经过这个代码,大家也可以自行写出一个多线程爬虫。

    整体的思路:

    1、构造任务队列pageQueue ,存放所有要爬取的页面url

    2、用多线程爬虫从糗事百科上抓取糗事,然后将抓取的页面内容存放到data_queue中

    3、用多线程程序对data_queue中的页面内容进行解析,分别提取 糗事的图片url,糗事的题目和糗事内容,

    然后存放到的json文件中(一个时间点只有一个线程可以写文件IO,注意到Python的多线程机制使用了GIL锁)

    import requests

    from lxml import etree

    from queue import Queue

    import threading

    import json

    '''

    Queue.qsize(队列名) #返回队列的大小

    Queue.empty(队列名) # 队列为空返回true,否则为false

    Queue.full(队列名) # 队列满返回true

    Queue.get(队列名,值) # 出队

    Queue.put(队列名,值) # 入队

    FIFO 先进先出

    '''

    class Crawl_thread(threading.Thread):

    '''

    抓取线程类,注意需要继承线程类Thread

    '''

    def __init__(self,thread_id,queue):

    threading.Thread.__init__(self)

    # 需要对父类的构造函数进行初始化

    self.thread_id = thread_id

    self.queue = queue # 任务队列

    def run(self):

    '''

    线程在调用过程中就会调用对应的run方法

    :return:

    '''

    print('启动线程:',self.thread_id)

    self.crawl_spider()

    print('退出了该线程:',self.thread_id)

    def crawl_spider(self):

    while True:

    if self.queue.empty(): #如果队列为空,则跳出

    break

    else:

    page = self.queue.get()

    print('当前工作的线程为:',self.thread_id,"

    正在采集:",page)

    url = 'https://www.qiushibaike.com/Shr/page/{}/'.

    format(str(page))headers = {

    'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36

    (KHTML, like Gecko)

    Chrome /67.0.3371.0 Safari/537.36'

    }

    try:

    content = requests.get(url,headers=headers)

    data_queue.put(content.text)

    # 将采集的结果放入data_queue中

    except Exception as e:

    print('采集线程错误',e)

    class Parser_thread(threading.Thread):

    '''

    解析网页的类,就是对采集结果进行解析,

    也是多线程方式进行解析

    '''

    def __init__(self,thread_id,queue,file):

    threading.Thread.__init__(self)

    self.thread_id = thread_id

    self.queue = queue

    self.file = file

    def run(self):

    print('启动线程:', self.thread_id)

    while not flag:

    try:

    item = self.queue.get(False)

    # get参数为false时队列为空,会抛出异常

    if not item:

    pass

    self.parse_data(item)

    self.queue.task_done()

    # 每当发出一次get操作,就会提示是否堵塞

    except Exception as e:

    pass

    print('退出了该线程:', self.thread_id)

    def parse_data(self,item):

    '''

    解析网页内容的函数

    :param item:

    :return:

    '''

    try:

    html = etree.HTML(item)

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

    # 匹配所有段子内容

    for site in result:

    try:

    img_url = site.xpath('.//img/@src')[0] # 糗事图片

    title = site.xpath('.//h2')[0].text # 糗事题目

    content = site.xpath('.//div[@class="content"]/span')[0].

    text.strip()

    # 糗事内容

    response={

    'img_url':img_url,

    'title':title,

    'content':content

    } #构造json

    json.dump(response,fp=self.file,ensure_ascii=False)

    # 存放json文件

    except Exception as e:

    print('parse 2: ', e)

    except Exception as e:

    print('parse 1: ',e)

    data_queue = Queue() # 存放解析数据的queue

    flag = False

    def main():

    output = open('qiushi.json','a',encoding='utf-8')

    # 将结果保存到一个json文件中

    pageQueue = Queue(50) # 任务队列,存放网页的队列

    for page in range(1,11):

    pageQueue.put(page) # 构造任务队列

    # 初始化采集线程

    crawl_threads = []

    crawl_name_list = ['crawl_1','crawl_2','crawl_3']

    # 总共构造3个爬虫线程

    for thread_id in crawl_name_list:

    thread = Crawl_thread(thread_id,pageQueue)

    #

    启动爬虫线程

    thread.start() # 启动线程

    crawl_threads.append(thread)

    # 初始化解析线程

    parse_thread = []

    parser_name_list = ['parse_1','parse_2','parse_3']

    for thread_id in parser_name_list: #

    thread = Parser_thread(thread_id,data_queue,output)

    thread.start() # 启动线程

    parse_thread.append(thread)

    # 等待队列情况,先进行网页的抓取

    while not pageQueue.empty(): # 判断是否为空

    pass # 不为空,则继续阻塞

    # 等待所有线程结束

    for t in crawl_threads:

    t.join()

    # 等待队列情况,对采集的页面队列中的页面进行解析,

    等待所有页面解析完成

    while not data_queue.empty():

    pass

    # 通知线程退出

    global flag

    flag = True

    for t in parse_thread:

    t.join() # 等待所有线程执行到此处再继续往下执行

    print('退出主线程')

    output.close()

    if __name__ == '__main__':

    main()

    展开全文
  • Python多线程爬虫 使用线程队列queue xpath解析页面 import requests from lxml import etree from queue import Queue import threading import time class Qiubai: def __init__(self): self.temp_url = ...
  • 题记:作为测试工程师经常需要解决测试数据来源的问题,解决思路无非是三种:(1)直接从生产环境拷贝...作为一个精益求精的测试工程师,决定研究一下多线程爬虫领域的应用,以提高爬虫的效率。一、为什么需要多线...
  • Python多线程|爬虫、爆破密码 干活酸爽
  • 本文按如下内容组织:# 介绍这个爬虫的目的、模块,创造原始单线程爬虫# 对这个爬虫进行改造,创造多线程爬虫一、爬虫功能介绍文章标题取的是《Python爬虫与多线程》,而不是《Python多线程爬虫》,因为爬虫的目的是...
  • python多线程爬虫

    2019-11-25 12:30:30
    python多线程爬虫 python单线程爬虫对于应付小规模数据是可以的,但是面对大量数据,我们就要用到多线程爬虫技术。使用多线程,一方面可能会加快效率,另一方面可以施加一些小技巧,如不同的线程使用不同的代理ip...
  • 主要介绍了Python如何使用队列方式实现多线程爬虫,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 虽然python多线程受GIL限制,并不是真正的多线程,但是对于I/O密集型计算还是能明显提高效率,比如说爬虫。分别用两种方式获取10个访问速度比较慢的网页,一般方式耗时50s,多线s。序:叮咚叮咚,...
  • python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补。并且在Python3中废弃了thread模块,保留了更强大的threading模块。使用场景在python的原始解释器CPython中存在...
  • 虽然python多线程受GIL限制,并不是真正的多线程,但是对于I/O密集型计算还是能明显提高效率,比如说爬虫。分别用两种方式获取10个访问速度比较慢的网页,一般方式耗时50s,多线s。序:叮咚叮咚,...
  • #仅供学习使用,如有侵权请留言删除from queue import Queueimport requestsfrom bs4 import BeautifulSoupimport timeimport threadingq = Queue()'''队列使用,.queue 查看队列内容.get() 获取队列内容.put()添加...
  • pycharm+chromedriver+bs4+re+threading+queue模拟登录小说多线程爬虫 首先要安装selenium,BeautifulSoup库,下载对应的chromedriver版本 一般在cmd里面pip install ×××,作者是用pycharm解释器的,里面可用搜索...
  • 我正在编写一个Python网络搜寻器,并且希望使其成为多线程的.现在我已经完成了基本部分,下面是它的作用:>线程从队列中获取URL;>线程从页面中提取链接,检查链接是否存在于一个池(一组)中,然后将新链接放入队列和池中...
  • python多线程图片爬虫

    千次阅读 2013-06-08 00:51:09
    这两天学习python,用python写了一个图片爬虫,顺便学习了一下多线程编程和正则表达式。 上代码: #!/usr/bin/env python # Time-stamp: # Email: import re import urllib import threading import time ...
  • 在上上篇文章讲多线程的时候附上了一则爬取腾讯招聘的多线程爬虫,也用到了队列,刚刚我做了写改动,就再写一篇文章把代码贴出来吧: class Tencent(Thread): #生产者线程,专门发送请求,然后将请求结果放入队列 ...
  • python多线程爬虫之练练手

    千次阅读 2017-04-30 19:13:55
    大数据时代,人工智能,机器学习开始流行了,赶上了这个时代是好的,也是不那么好的,好的是将会为社会带来革命性的洗礼,不那么好的是感觉出生还是太早了点哈哈哈,要是晚几十年生,等我风华正茂的时候,那是壮观...
  • python3直接queue即可 在python中,线程之间的数据是共享的,线程进行数据交换的时候,不能够保证数据的安全性和一致性,所以当线程需要进行数据交...相关推荐2019-12-11 18:28 −cen...
  • 100行代码教你爬取斗图网(Python多线程队列) 前言 根据之前写的两篇文章,想必大家对多线程和队列有了一个初步的了解,今天这篇文章就来实战一下,用多线程 + 队列 爬取斗图网的全网图片。 你还在为斗图找图片而...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,466
精华内容 3,386
关键字:

python多线程队列爬虫

python 订阅
爬虫 订阅