一个简单的多线程Python爬虫

最近想要抓取拉勾网的数据,最开始是使用Scrapy的,但是遇到了下面两个问题:

  1. 前端页面是用JS模板引擎生成的
  2. 接口主要是用POST提交参数的

目前不会处理使用JS模板引擎生成的HTML页面,用POST的提交参数的话,接口统一,也没有必要使用Scrapy,所以就萌生了自己写一个简单的Python爬虫的想法。

本文中的部分链接可能需要翻墙。

参考资料:

  1. http://www.ibm.com/developerworks/aix/library/au-threadingpython/
  2. http://stackoverflow.com/questions/10525185/python-threading-how-do-i-lock-a-thread

一个爬虫的简单框架

一个简单的爬虫框架,主要就是处理网络请求,Scrapy使用的是Twisted(一个事件驱动网络框架,以非阻塞的方式对网络I/O进行异步处理),这里不使用异步处理,等以后再研究这个框架。如果使用的是Python3.4及其以上版本,到可以使用asyncio这个标准库。

这个简单的爬虫使用多线程来处理网络请求,使用线程来处理URL队列中的url,然后将url返回的结果保存在另一个队列中,其它线程在读取这个队列中的数据,然后写到文件中去。

该爬虫主要用下面几个部分组成。

1 URL队列和结果队列

将将要爬去的url放在一个队列中,这里使用标准库Queue。访问url后的结果保存在结果队列中

初始化一个URL队列

  1. from Queue import Queue
  2. urls_queue = Queue()
  3. out_queue = Queue()

2 请求线程

使用多个线程,不停的取URL队列中的url,并进行处理:

  1. import threading
  2. class ThreadCrawl(threading.Thread):
  3. def __init__(self, queue, out_queue):
  4. threading.Thread.__init__(self)
  5. self.queue = queue
  6. self.out_queue = out_queue
  7. def run(self):
  8. while True:
  9. item = self.queue.get()
  10. self.queue.task_down()

下面是部分标准库Queue的使用方法:

Queue.get([block[, timeout]])

Remove and return an item from the queue. If optional args block is true and timeout is None (the default), block if necessary until an item is available.

Queue.task_done()

Indicate that a formerly enqueued task is complete. Used by queue consumer threads. For each get() used to fetch a task, a subsequent call to task_done() tells the queue that the processing on the task is complete.

如果队列为空,线程就会被阻塞,直到队列不为空。处理队列中的一条数据后,就需要通知队列已经处理完该条数据。

处理线程

处理结果队列中的数据,并保存到文件中。如果使用多个线程的话,必须要给文件加上锁。

  1. lock = threading.Lock()
  2. f = codecs.open('out.txt', 'w', 'utf8')

当线程需要写入文件的时候,可以这样处理:

  1. with lock:
  2. f.write(something)

程序的执行结果

运行状态:

抓取结果:

源码

代码还不完善,将会持续修改中。

  1. # coding: utf-8
  2. '''
  3. Author mr_zys
  4. Email myzysv5@sina.com
  5. '''
  6. from Queue import Queue
  7. import threading
  8. import urllib2
  9. import time
  10. import json
  11. import codecs
  12. from bs4 import BeautifulSoup
  13. urls_queue = Queue()
  14. data_queue = Queue()
  15. lock = threading.Lock()
  16. f = codecs.open('out.txt', 'w', 'utf8')
  17. class ThreadUrl(threading.Thread):
  18. def __init__(self, queue):
  19. threading.Thread.__init__(self)
  20. self.queue = queue
  21. def run(self):
  22. pass
  23. class ThreadCrawl(threading.Thread):
  24. def __init__(self, url, queue, out_queue):
  25. threading.Thread.__init__(self)
  26. self.url = url
  27. self.queue = queue
  28. self.out_queue = out_queue
  29. def run(self):
  30. while True:
  31. item = self.queue.get()
  32. data = self._data_post(item)
  33. try:
  34. req = urllib2.Request(url=self.url, data=data)
  35. res = urllib2.urlopen(req)
  36. except urllib2.HTTPError, e:
  37. raise e.reason
  38. py_data = json.loads(res.read())
  39. res.close()
  40. item['first'] = 'false'
  41. item['pn'] = item['pn'] + 1
  42. success = py_data['success']
  43. if success:
  44. print 'Get success...'
  45. else:
  46. print 'Get fail....'
  47. print 'pn is : %s' % item['pn']
  48. result = py_data['content']['result']
  49. if len(result) != 0:
  50. self.queue.put(item)
  51. print 'now queue size is: %d' % self.queue.qsize()
  52. self.out_queue.put(py_data['content']['result'])
  53. self.queue.task_done()
  54. def _data_post(self, item):
  55. pn = item['pn']
  56. first = 'false'
  57. if pn == 1:
  58. first = 'true'
  59. return 'first=' + first + '&pn=' + str(pn) + '&kd=' + item['kd']
  60. def _item_queue(self):
  61. pass
  62. class ThreadWrite(threading.Thread):
  63. def __init__(self, queue, lock, f):
  64. threading.Thread.__init__(self)
  65. self.queue = queue
  66. self.lock = lock
  67. self.f = f
  68. def run(self):
  69. while True:
  70. item = self.queue.get()
  71. self._parse_data(item)
  72. self.queue.task_done()
  73. def _parse_data(self, item):
  74. for i in item:
  75. l = self._item_to_str(i)
  76. with self.lock:
  77. print 'write %s' % l
  78. self.f.write(l)
  79. def _item_to_str(self, item):
  80. positionName = item['positionName']
  81. positionType = item['positionType']
  82. workYear = item['workYear']
  83. education = item['education']
  84. jobNature = item['jobNature']
  85. companyName = item['companyName']
  86. companyLogo = item['companyLogo']
  87. industryField = item['industryField']
  88. financeStage = item['financeStage']
  89. companyShortName = item['companyShortName']
  90. city = item['city']
  91. salary = item['salary']
  92. positionFirstType = item['positionFirstType']
  93. createTime = item['createTime']
  94. positionId = item['positionId']
  95. return positionName + ' ' + positionType + ' ' + workYear + ' ' + education + ' ' + \
  96. jobNature + ' ' + companyLogo + ' ' + industryField + ' ' + financeStage + ' ' + \
  97. companyShortName + ' ' + city + ' ' + salary + ' ' + positionFirstType + ' ' + \
  98. createTime + ' ' + str(positionId) + '\n'
  99. def main():
  100. for i in range(4):
  101. t = ThreadCrawl(
  102. 'http://www.lagou.com/jobs/positionAjax.json', urls_queue, data_queue)
  103. t.setDaemon(True)
  104. t.start()
  105. datas = [
  106. {'first': 'true', 'pn': 1, 'kd': 'Java'}
  107. #{'first': 'true', 'pn': 1, 'kd': 'Python'}
  108. ]
  109. for d in datas:
  110. urls_queue.put(d)
  111. for i in range(4):
  112. t = ThreadWrite(data_queue, lock, f)
  113. t.setDaemon(True)
  114. t.start()
  115. urls_queue.join()
  116. data_queue.join()
  117. with lock:
  118. f.close()
  119. print 'data_queue siez: %d' % data_queue.qsize()
  120. main()

总结

主要是熟悉使用Python的多线程编程,以及一些标准库的使用Queuethreading

一个简单的多线程Python爬虫(一)的更多相关文章

  1. 一个简单的定向python爬虫爬取指定页面的jpg图片

    import requests as r import re resul=r.get("http://www.imooc.com/course/list") urlinfo=re. ...

  2. Java Tread多线程(0)一个简单的多线程实例

    作者 : 卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39341887 本文演示,一个简单的多线程实例,并简单分析一下线程. 编程多 ...

  3. Qt5.9一个简单的多线程实例(类QThread)(第一种方法)

    Qt开启多线程,主要用到类QThread.有两种方法,第一种用一个类继承QThread,然后重新改写虚函数run().当要开启新线程时,只需要实例该类,然后调用函数start(),就可以开启一条多线程 ...

  4. 实现一个简单的邮箱地址爬虫(python)

    我经常收到关于email爬虫的问题.有迹象表明那些想从网页上抓取联系方式的人对这个问题很感兴趣.在这篇文章里,我想演示一下如何使用python实现一个简单的邮箱爬虫.这个爬虫很简单,但从这个例子中你可 ...

  5. 一个简单的开源PHP爬虫框架『Phpfetcher』

    这篇文章首发在吹水小镇:http://blog.reetsee.com/archives/366 要在手机或者电脑看到更好的图片或代码欢迎到博文原地址.也欢迎到博文原地址批评指正. 转载请注明: 吹水 ...

  6. 【原创】编写多线程Python爬虫来过滤八戒网上的发布任务

    目标: 以特定语言技术为关键字,爬取八戒网中网站设计开发栏目下发布的任务相关信息 需求: 用户通过设置自己感兴趣的关键字或正则表达式,来过滤信息. 我自己选择的是通过特定语言技术作为关键字,php.j ...

  7. 一个简单的用python 实现系统登录的http接口服务实例

    用python 开发一个登录的http接口: 用户登录数据存在缓存redis里,登录时根据session判断用户是否已登录,session有效,则直接返回用户已登录,否则进mysql查询用户名及密码, ...

  8. MAC COCOA一个简单的多线程程序

    功能: 实现多线程:2个线程同一时候工作,一个用时间计数器.一个用来信息打印 STEP1 XCODE ->New Application ->Cocoa中的Command Line 自己主 ...

  9. 一个简单有趣的Python音乐播放器

    (赠新手,老鸟绕行0.0) Python版本:3.5.2 源码如下: __Author__ = "Lance#" # -*- coding = utf-8 -*- #导入相应模块 ...

随机推荐

  1. hdoj 2524 矩形A + B【递推】

    矩形A + B Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  2. javascript 的点击复制事件

    function copy() { var text=document.getElementById("txtUser").value; if(copy2Clipboard(tex ...

  3. UVa1606 UVaLive3259 FZU1309 HDU1661 POJ2280 ZOJ2390 Amphiphilic Carbon Molecules

    填坑系列 考虑所有经过两个点的直线,一定有最优解. 再考虑确定一个点,按极角顺序枚举所有直线,从而O(1)转移信息. 还有代码实现技巧 #include<cstdio> #include& ...

  4. C# 字符串常用操作 分类: C# 2014-08-22 15:07 238人阅读 评论(0) 收藏

    string str1 = "C#操作字符串<几种常见方式>如下"; string str2 = "C#操作字符串";     //比较字符串 Co ...

  5. 查看mysql数据库及表编码格式

    1.查看数据库编码格式 mysql> show variables like 'character_set_database'; 2.查看数据表的编码格式 mysql> show crea ...

  6. 【LeetCode】Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  7. Qt 学习之路 :使用 QJson 处理 JSON

    XML 曾经是各种应用的配置和传输的首选方式.但是现在 XML 遇到了一个强劲的对手:JSON.我们可以在 这里 看到有关 JSON 的语法.总体来说,JSON 的数据比 XML 更紧凑,在传输效率上 ...

  8. Annotation Type @bean,@Import,@configuration使用--官方文档

    @Target(value={METHOD,ANNOTATION_TYPE}) @Retention(value=RUNTIME) @Documented public @interface Bean ...

  9. 观察者模式在ng(Angular)中的应用

    在这个前端框架满天飞的天下,angular MVVM 的模式确实火了一把,所以最近一直在学习ng,感悟颇多,填坑无数,今天终静下心来打算更新自己久未变动的博客,做一做总结. 1.在ng中的观察者模式: ...

  10. shell入门之流程控制语句 分类: 学习笔记 linux ubuntu 2015-07-10 16:38 89人阅读 评论(0) 收藏

    1.case 脚本: #!/bin/bash #a test about case case $1 in "lenve") echo "input lenve" ...