案例要求参考上一个糗事百科单进程案例

Queue(队列对象)

Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式

python下多线程的思考

对于资源,加锁是个重要的环节。因为python原生的list,dict等,都是not thread safe的。而Queue,是线程安全的,因此在满足使用条件下,建议使用队列

  1. 初始化: class Queue.Queue(maxsize) FIFO 先进先出

  2. 包中的常用方法:

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

    • Queue.empty() 如果队列为空,返回True,反之False

    • Queue.full() 如果队列满了,返回True,反之False

    • Queue.full 与 maxsize 大小对应

    • Queue.get([block[, timeout]])获取队列,timeout等待时间

  3. 创建一个“队列”对象

    • import Queue
    • myqueue = Queue.Queue(maxsize = 10)
  4. 将一个值放入队列中

    • myqueue.put(10)
  5. 将一个值从队列中取出

    • myqueue.get()

多线程示意图

  1. # -*- coding:utf-8 -*-
  2. import requests
  3. from lxml import etree
  4. from Queue import Queue
  5. import threading
  6. import time
  7. import json
  8. class thread_crawl(threading.Thread):
  9. '''
  10. 抓取线程类
  11. '''
  12. def __init__(self, threadID, q):
  13. threading.Thread.__init__(self)
  14. self.threadID = threadID
  15. self.q = q
  16. def run(self):
  17. print "Starting " + self.threadID
  18. self.qiushi_spider()
  19. print "Exiting ", self.threadID
  20. def qiushi_spider(self):
  21. # page = 1
  22. while True:
  23. if self.q.empty():
  24. break
  25. else:
  26. page = self.q.get()
  27. print 'qiushi_spider=', self.threadID, ',page=', str(page)
  28. url = 'http://www.qiushibaike.com/8hr/page/' + str(page) + '/'
  29. headers = {
  30. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
  31. 'Accept-Language': 'zh-CN,zh;q=0.8'}
  32. # 多次尝试失败结束、防止死循环
  33. timeout = 4
  34. while timeout > 0:
  35. timeout -= 1
  36. try:
  37. content = requests.get(url, headers=headers)
  38. data_queue.put(content.text)
  39. break
  40. except Exception, e:
  41. print 'qiushi_spider', e
  42. if timeout < 0:
  43. print 'timeout', url
  44. class Thread_Parser(threading.Thread):
  45. '''
  46. 页面解析类;
  47. '''
  48. def __init__(self, threadID, queue, lock, f):
  49. threading.Thread.__init__(self)
  50. self.threadID = threadID
  51. self.queue = queue
  52. self.lock = lock
  53. self.f = f
  54. def run(self):
  55. print 'starting ', self.threadID
  56. global total, exitFlag_Parser
  57. while not exitFlag_Parser:
  58. try:
  59. '''
  60. 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。
  61. 如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。
  62. 如果队列为空且block为False,队列将引发Empty异常。
  63. '''
  64. item = self.queue.get(False)
  65. if not item:
  66. pass
  67. self.parse_data(item)
  68. self.queue.task_done()
  69. print 'Thread_Parser=', self.threadID, ',total=', total
  70. except:
  71. pass
  72. print 'Exiting ', self.threadID
  73. def parse_data(self, item):
  74. '''
  75. 解析网页函数
  76. :param item: 网页内容
  77. :return:
  78. '''
  79. global total
  80. try:
  81. html = etree.HTML(item)
  82. result = html.xpath('//div[contains(@id,"qiushi_tag")]')
  83. for site in result:
  84. try:
  85. imgUrl = site.xpath('.//img/@src')[0]
  86. title = site.xpath('.//h2')[0].text
  87. content = site.xpath('.//div[@class="content"]/span')[0].text.strip()
  88. vote = None
  89. comments = None
  90. try:
  91. vote = site.xpath('.//i')[0].text
  92. comments = site.xpath('.//i')[1].text
  93. except:
  94. pass
  95. result = {
  96. 'imgUrl': imgUrl,
  97. 'title': title,
  98. 'content': content,
  99. 'vote': vote,
  100. 'comments': comments,
  101. }
  102. with self.lock:
  103. # print 'write %s' % json.dumps(result)
  104. self.f.write(json.dumps(result, ensure_ascii=False).encode('utf-8') + "\n")
  105. except Exception, e:
  106. print 'site in result', e
  107. except Exception, e:
  108. print 'parse_data', e
  109. with self.lock:
  110. total += 1
  111. data_queue = Queue()
  112. exitFlag_Parser = False
  113. lock = threading.Lock()
  114. total = 0
  115. def main():
  116. output = open('qiushibaike.json', 'a')
  117. #初始化网页页码page从1-10个页面
  118. pageQueue = Queue(50)
  119. for page in range(1, 11):
  120. pageQueue.put(page)
  121. #初始化采集线程
  122. crawlthreads = []
  123. crawlList = ["crawl-1", "crawl-2", "crawl-3"]
  124. for threadID in crawlList:
  125. thread = thread_crawl(threadID, pageQueue)
  126. thread.start()
  127. crawlthreads.append(thread)
  128. #初始化解析线程parserList
  129. parserthreads = []
  130. parserList = ["parser-1", "parser-2", "parser-3"]
  131. #分别启动parserList
  132. for threadID in parserList:
  133. thread = Thread_Parser(threadID, data_queue, lock, output)
  134. thread.start()
  135. parserthreads.append(thread)
  136. # 等待队列清空
  137. while not pageQueue.empty():
  138. pass
  139. # 等待所有线程完成
  140. for t in crawlthreads:
  141. t.join()
  142. while not data_queue.empty():
  143. pass
  144. # 通知线程是时候退出
  145. global exitFlag_Parser
  146. exitFlag_Parser = True
  147. for t in parserthreads:
  148. t.join()
  149. print "Exiting Main Thread"
  150. with lock:
  151. output.close()
  152. if __name__ == '__main__':
  153. main()
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. # 使用了线程库
  5. import threading
  6. # 队列
  7. from Queue import Queue
  8. # 解析库
  9. from lxml import etree
  10. # 请求处理
  11. import requests
  12. # json处理
  13. import json
  14. import time
  15.  
  16. class ThreadCrawl(threading.Thread):
  17. def __init__(self, threadName, pageQueue, dataQueue):
  18. #threading.Thread.__init__(self)
  19. # 调用父类初始化方法
  20. super(ThreadCrawl, self).__init__()
  21. # 线程名
  22. self.threadName = threadName
  23. # 页码队列
  24. self.pageQueue = pageQueue
  25. # 数据队列
  26. self.dataQueue = dataQueue
  27. # 请求报头
  28. self.headers = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
  29.  
  30. def run(self):
  31. print "启动 " + self.threadName
  32. while not CRAWL_EXIT:
  33. try:
  34. # 取出一个数字,先进先出
  35. # 可选参数block,默认值为True
  36. #1. 如果对列为空,block为True的话,不会结束,会进入阻塞状态,直到队列有新的数据
  37. #2. 如果队列为空,block为False的话,就弹出一个Queue.empty()异常,
  38. page = self.pageQueue.get(False)
  39. url = "http://www.qiushibaike.com/8hr/page/" + str(page) +"/"
  40. #print url
  41. content = requests.get(url, headers = self.headers).text
  42. time.sleep(1)
  43. self.dataQueue.put(content)
  44. #print len(content)
  45. except:
  46. pass
  47. print "结束 " + self.threadName
  48.  
  49. class ThreadParse(threading.Thread):
  50. def __init__(self, threadName, dataQueue, filename, lock):
  51. super(ThreadParse, self).__init__()
  52. # 线程名
  53. self.threadName = threadName
  54. # 数据队列
  55. self.dataQueue = dataQueue
  56. # 保存解析后数据的文件名
  57. self.filename = filename
  58. # 锁
  59. self.lock = lock
  60.  
  61. def run(self):
  62. print "启动" + self.threadName
  63. while not PARSE_EXIT:
  64. try:
  65. html = self.dataQueue.get(False)
  66. self.parse(html)
  67. except:
  68. pass
  69. print "退出" + self.threadName
  70.  
  71. def parse(self, html):
  72. # 解析为HTML DOM
  73. html = etree.HTML(html)
  74.  
  75. node_list = html.xpath('//div[contains(@id, "qiushi_tag")]')
  76.  
  77. for node in node_list:
  78. # xpath返回的列表,这个列表就这一个参数,用索引方式取出来,用户名
  79. username = node.xpath('./div/a/@title')[0]
  80. # 图片连接
  81. image = node.xpath('.//div[@class="thumb"]//@src')#[0]
  82. # 取出标签下的内容,段子内容
  83. content = node.xpath('.//div[@class="content"]/span')[0].text
  84. # 取出标签里包含的内容,点赞
  85. zan = node.xpath('.//i')[0].text
  86. # 评论
  87. comments = node.xpath('.//i')[1].text
  88.  
  89. items = {
  90. "username" : username,
  91. "image" : image,
  92. "content" : content,
  93. "zan" : zan,
  94. "comments" : comments
  95. }
  96.  
  97. # with 后面有两个必须执行的操作:__enter__ 和 _exit__
  98. # 不管里面的操作结果如何,都会执行打开、关闭
  99. # 打开锁、处理内容、释放锁
  100. with self.lock:
  101. # 写入存储的解析后的数据
  102. self.filename.write(json.dumps(items, ensure_ascii = False).encode("utf-8") + "\n")
  103.  
  104. CRAWL_EXIT = False
  105. PARSE_EXIT = False
  106.  
  107. def main():
  108. # 页码的队列,表示20个页面
  109. pageQueue = Queue(20)
  110. # 放入1~10的数字,先进先出
  111. for i in range(1, 21):
  112. pageQueue.put(i)
  113.  
  114. # 采集结果(每页的HTML源码)的数据队列,参数为空表示不限制
  115. dataQueue = Queue()
  116.  
  117. filename = open("duanzi.json", "a")
  118. # 创建锁
  119. lock = threading.Lock()
  120.  
  121. # 三个采集线程的名字
  122. crawlList = ["采集线程1号", "采集线程2号", "采集线程3号"]
  123. # 存储三个采集线程的列表集合
  124. threadcrawl = []
  125. for threadName in crawlList:
  126. thread = ThreadCrawl(threadName, pageQueue, dataQueue)
  127. thread.start()
  128. threadcrawl.append(thread)
  129.  
  130. # 三个解析线程的名字
  131. parseList = ["解析线程1号","解析线程2号","解析线程3号"]
  132. # 存储三个解析线程
  133. threadparse = []
  134. for threadName in parseList:
  135. thread = ThreadParse(threadName, dataQueue, filename, lock)
  136. thread.start()
  137. threadparse.append(thread)
  138.  
  139. # 等待pageQueue队列为空,也就是等待之前的操作执行完毕
  140. while not pageQueue.empty():
  141. pass
  142.  
  143. # 如果pageQueue为空,采集线程退出循环
  144. global CRAWL_EXIT
  145. CRAWL_EXIT = True
  146.  
  147. print "pageQueue为空"
  148.  
  149. for thread in threadcrawl:
  150. thread.join()
  151. print "1"
  152.  
  153. while not dataQueue.empty():
  154. pass
  155.  
  156. global PARSE_EXIT
  157. PARSE_EXIT = True
  158.  
  159. for thread in threadparse:
  160. thread.join()
  161. print "2"
  162.  
  163. with lock:
  164. # 关闭文件
  165. filename.close()
  166. print "谢谢使用!"
  167.  
  168. if __name__ == "__main__":
  169. main()

  

  1.  

python 多线程糗事百科案例的更多相关文章

  1. Python爬虫(十八)_多线程糗事百科案例

    多线程糗事百科案例 案例要求参考上一个糗事百科单进程案例:http://www.cnblogs.com/miqi1992/p/8081929.html Queue(队列对象) Queue是python ...

  2. Python爬虫(十七)_糗事百科案例

    糗事百科实例 爬取糗事百科段子,假设页面的URL是: http://www.qiushibaike.com/8hr/page/1 要求: 使用requests获取页面信息,用XPath/re做数据提取 ...

  3. python 爬糗事百科

    糗事百科网站段子爬取,糗事百科是我见过的最简单的网站了!!! #-*-coding:utf8-*- import requests import re import sys reload(sys) s ...

  4. Python 之糗事百科多线程爬虫案例

    import requests from lxml import etree import json import threading import queue # 采集html类 class Get ...

  5. (python)查看糗事百科文字 点赞 作者 等级 评论

    import requestsimport reheaders = { 'User-Agent':'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; ...

  6. 【Python爬虫实战】多线程爬虫---糗事百科段子爬取

    多线程爬虫:即程序中的某些程序段并行执行,合理地设置多线程,可以让爬虫效率更高糗事百科段子普通爬虫和多线程爬虫分析该网址链接得出:https://www.qiushibaike.com/8hr/pag ...

  7. Python爬虫爬取糗事百科段子内容

    参照网上的教程再做修改,抓取糗事百科段子(去除图片),详情见下面源码: #coding=utf-8#!/usr/bin/pythonimport urllibimport urllib2import ...

  8. 利用python的爬虫技术爬去糗事百科的段子

    初次学习爬虫技术,在知乎上看了如何爬去糗事百科的段子,于是打算自己也做一个. 实现目标:1,爬取到糗事百科的段子 2,实现每次爬去一个段子,每按一次回车爬取到下一页 技术实现:基于python的实现, ...

  9. python 爬取糗事百科 gui小程序

    前言:有时候无聊看一些搞笑的段子,糗事百科还是个不错的网站,所以就想用Python来玩一下.也比较简单,就写出来分享一下.嘿嘿 环境:Python 2.7 + win7 现在开始,打开糗事百科网站,先 ...

随机推荐

  1. UIView的transform属性

    一.什么是Transform Transform(变化矩阵)是一种3×3的矩阵,如下图所示: 通过这个矩阵我们可以对一个坐标系统进行缩放,平移,旋转以及这两者的任意组着操作.而且矩阵的操作不具备交换律 ...

  2. vue - node_modules

    详情见:node_modules导包机制 在打包或者结束项目时,这个文件夹(node_modules)不应该被打包. 你应该打包其它的文件,如果要运行(直接用以下命令安装即可,它会根据package. ...

  3. 在Docker中执行web应用

    启动一个简单的web 应用 使用社区提供的模板,启动一个简单的web应用,熟悉下各种Docker命令的使用: # docker run -d -P training/webapp python app ...

  4. Linux-查看进程的完整路径

    通过ps及top命令查看进程信息时,只能查到相对路径,查不到的进程的详细信息,如绝对路径等.这时,我们需要通过以下的方法来查看进程的详细信息:Linux在启动一个进程时,系统会在/proc下创建一个以 ...

  5. js jquery 结束循环

    js 中跳出循环用break,结束本次循环用continue,jqeruy 中循环分别对应 return false 和return true. jquery 中each循环 跳出用return tr ...

  6. VMware配置网络的3种方式:NAT、Host-Only、Bridged

    网络常识: 1.网络中对电脑的访问是通过ip定位的 就好像我们的身份证号,可以唯一辨识一个人.ip是用来区分网络中的电脑的,因此同一网络(准确讲是“网段”)中,ip地址不能相同.如果同一网络中有相同的 ...

  7. Kafka 快速起步

    Kafka 快速起步 原创 2017-01-05 杜亦舒 性能与架构 性能与架构 性能与架构 微信号 yogoup 功能介绍 网站性能提升与架构设计 主要内容:1. kafka 安装.启动2. 消息的 ...

  8. unity, trail renderer gone black on iOS

    给物体加了个trail renderer,使用了Legacy Shaders/Transparent/Diffuse,并将颜色调成白色半透明.在编辑器里效果是对的,但在ios上真机测试变成黑色的.然后 ...

  9. hsqldb

    http://www.hsqldb.org/ HSQLDB (HyperSQL DataBase) is the leading SQL relational database software wr ...

  10. 详解Java中格式化日期的DateFormat与SimpleDateFormat类

    DateFormat其本身是一个抽象类,SimpleDateFormat 类是DateFormat类的子类,一般情况下来讲DateFormat类很少会直接使用,而都使用SimpleDateFormat ...