案例_(多线程)爬取糗事百科

清疚 2022-05-13 00:56 291阅读 0赞

讲解都写在代码注释中了,直接上代码

  1. # 使用了线程库
  2. import threading
  3. # 队列
  4. from queue import Queue
  5. # 解析库
  6. from lxml import etree
  7. # 请求处理
  8. import requests
  9. # json处理
  10. import time
  11. class ThreadCrawl(threading.Thread):
  12. def __init__(self, thread_name, page_queue, data_queue):
  13. # threading.Thread.__init__(self)
  14. # 调用父类初始化方法
  15. super(ThreadCrawl, self).__init__()
  16. # 线程名
  17. self.thread_name = thread_name
  18. # 页码队列
  19. self.page_queue = page_queue
  20. # 数据队列
  21. self.data_queue = data_queue
  22. # 请求报头
  23. self.headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
  24. def run(self):
  25. print("启动 " + self.thread_name)
  26. while not CRAWL_EXIT:
  27. try:
  28. # 取出一个数字,先进先出
  29. # 可选参数block,默认值为True
  30. # 1. 如果对列为空,block为True的话,不会结束,会进入阻塞状态,直到队列有新的数据
  31. # 2. 如果队列为空,block为False的话,就弹出一个Queue.empty()异常,
  32. page = self.page_queue.get(False)
  33. url = "http://www.qiushibaike.com/8hr/page/" + str(page) + "/"
  34. # print url
  35. content = requests.get(url, headers=self.headers).text
  36. time.sleep(1)
  37. self.data_queue.put(content)
  38. # print len(content)
  39. except:
  40. pass
  41. print("结束 " + self.thread_name)
  42. class ThreadParse(threading.Thread):
  43. def __init__(self, thread_name, data_queue):
  44. super(ThreadParse, self).__init__()
  45. # 线程名
  46. self.thread_name = thread_name
  47. # 数据队列
  48. self.data_queue = data_queue
  49. def run(self):
  50. print("启动" + self.thread_name)
  51. while not PARSE_EXIT:
  52. try:
  53. html = self.data_queue.get(False)
  54. self.parse(html)
  55. except:
  56. pass
  57. print("退出" + self.thread_name)
  58. def parse(self, html):
  59. selector = etree.HTML(html)
  60. # 返回所有段子的节点位置,contant()模糊查询方法,第一个参数是要匹配的标签,第二个参数是这个标签的部分内容
  61. # 每个节点包括一条完整的段子(用户名,段子内容,点赞,评论等)
  62. node_list = selector.xpath('//div[contains(@id,"qiushi_tag_")]')
  63. for node in node_list:
  64. # 爬取所有用户名信息
  65. # 取出标签里的内容,使用.text方法
  66. user_name = node.xpath('./div[@class="author clearfix"]//h2')[0].text
  67. # 爬取段子内容,匹配规则必须加点 不然还是会从整个页面开始匹配
  68. # 注意:如果span标签中有br 在插件中没问题,在代码中会把br也弄进来
  69. duanzi_info = node.xpath('.//div[@class="content"]/span')[0].text.strip()
  70. # 爬取段子的点赞数
  71. vote_num = node.xpath('.//span[@class="stats-vote"]/i')[0].text
  72. # 爬取评论数
  73. comment_num = node.xpath('.//span[@class="stats-comments"]//i')[0].text
  74. # 爬取图片链接
  75. # 属性src的值,所以不需要.text
  76. img_url = node.xpath('.//div[@class="thumb"]//@src')
  77. if len(img_url) > 0:
  78. img_url = img_url[0]
  79. else:
  80. img_url = "无图片"
  81. self.save_info(user_name, duanzi_info, vote_num, comment_num, img_url)
  82. def save_info(self, user_name, duanzi_info, vote_num, comment_num, img_url):
  83. """把每条段子的相关信息写进字典"""
  84. item = {
  85. "username": user_name,
  86. "content": duanzi_info,
  87. "zan": vote_num,
  88. "comment": comment_num,
  89. "image_url": img_url
  90. }
  91. print(item)
  92. CRAWL_EXIT = False
  93. PARSE_EXIT = False
  94. def main():
  95. # 页码的队列,表示20个页面
  96. pageQueue = Queue(20)
  97. # 放入1~10的数字,先进先出
  98. for i in range(1, 21):
  99. pageQueue.put(i)
  100. # 采集结果(每页的HTML源码)的数据队列,参数为空表示不限制
  101. dataQueue = Queue()
  102. filename = open("duanzi.json", "a")
  103. # 创建锁
  104. lock = threading.Lock()
  105. # 三个采集线程的名字
  106. crawlList = ["采集线程1号", "采集线程2号", "采集线程3号"]
  107. # 存储三个采集线程的列表集合
  108. thread_crawl = []
  109. for threadName in crawlList:
  110. thread = ThreadCrawl(threadName, pageQueue, dataQueue)
  111. thread.start()
  112. thread_crawl.append(thread)
  113. # 三个解析线程的名字
  114. parseList = ["解析线程1号", "解析线程2号", "解析线程3号"]
  115. # 存储三个解析线程
  116. thread_parse = []
  117. for threadName in parseList:
  118. thread = ThreadParse(threadName, dataQueue)
  119. thread.start()
  120. thread_parse.append(thread)
  121. # 等待pageQueue队列为空,也就是等待之前的操作执行完毕
  122. while not pageQueue.empty():
  123. pass
  124. # 如果pageQueue为空,采集线程退出循环
  125. global CRAWL_EXIT
  126. CRAWL_EXIT = True
  127. print("pageQueue为空")
  128. for thread in thread_crawl:
  129. thread.join()
  130. while not dataQueue.empty():
  131. pass
  132. global PARSE_EXIT
  133. PARSE_EXIT = True
  134. for thread in thread_parse:
  135. thread.join()
  136. if __name__ == "__main__":
  137. main()

爬取结果如下:

70

如果你和我有共同爱好,我们可以加个好友一起交流!

1445380-20180903211901788-1752749014.png

发表评论

表情:
评论列表 (有 0 条评论,291人围观)

还没有评论,来说两句吧...

相关阅读