一个简单的多线程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队列

from Queue import Queue
urls_queue = Queue()
out_queue = Queue()

2 请求线程

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

import threading

class ThreadCrawl(threading.Thread):
def __init__(self, queue, out_queue):
threading.Thread.__init__(self)
self.queue = queue
self.out_queue = out_queue def run(self):
while True:
item = self.queue.get()
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.

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

处理线程

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

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

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

with lock:
f.write(something)

程序的执行结果

运行状态:

抓取结果:

源码

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

# coding: utf-8
'''
Author mr_zys Email myzysv5@sina.com
''' from Queue import Queue
import threading
import urllib2
import time
import json
import codecs
from bs4 import BeautifulSoup urls_queue = Queue()
data_queue = Queue()
lock = threading.Lock()
f = codecs.open('out.txt', 'w', 'utf8') class ThreadUrl(threading.Thread): def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue def run(self):
pass class ThreadCrawl(threading.Thread): def __init__(self, url, queue, out_queue):
threading.Thread.__init__(self)
self.url = url
self.queue = queue
self.out_queue = out_queue def run(self):
while True:
item = self.queue.get()
data = self._data_post(item)
try:
req = urllib2.Request(url=self.url, data=data)
res = urllib2.urlopen(req)
except urllib2.HTTPError, e:
raise e.reason
py_data = json.loads(res.read())
res.close()
item['first'] = 'false'
item['pn'] = item['pn'] + 1
success = py_data['success']
if success:
print 'Get success...'
else:
print 'Get fail....'
print 'pn is : %s' % item['pn']
result = py_data['content']['result']
if len(result) != 0:
self.queue.put(item)
print 'now queue size is: %d' % self.queue.qsize()
self.out_queue.put(py_data['content']['result'])
self.queue.task_done() def _data_post(self, item):
pn = item['pn']
first = 'false'
if pn == 1:
first = 'true'
return 'first=' + first + '&pn=' + str(pn) + '&kd=' + item['kd'] def _item_queue(self):
pass class ThreadWrite(threading.Thread): def __init__(self, queue, lock, f):
threading.Thread.__init__(self)
self.queue = queue
self.lock = lock
self.f = f def run(self):
while True:
item = self.queue.get()
self._parse_data(item)
self.queue.task_done() def _parse_data(self, item):
for i in item:
l = self._item_to_str(i)
with self.lock:
print 'write %s' % l
self.f.write(l) def _item_to_str(self, item):
positionName = item['positionName']
positionType = item['positionType']
workYear = item['workYear']
education = item['education']
jobNature = item['jobNature']
companyName = item['companyName']
companyLogo = item['companyLogo']
industryField = item['industryField']
financeStage = item['financeStage']
companyShortName = item['companyShortName']
city = item['city']
salary = item['salary']
positionFirstType = item['positionFirstType']
createTime = item['createTime']
positionId = item['positionId']
return positionName + ' ' + positionType + ' ' + workYear + ' ' + education + ' ' + \
jobNature + ' ' + companyLogo + ' ' + industryField + ' ' + financeStage + ' ' + \
companyShortName + ' ' + city + ' ' + salary + ' ' + positionFirstType + ' ' + \
createTime + ' ' + str(positionId) + '\n' def main():
for i in range(4):
t = ThreadCrawl(
'http://www.lagou.com/jobs/positionAjax.json', urls_queue, data_queue)
t.setDaemon(True)
t.start()
datas = [
{'first': 'true', 'pn': 1, 'kd': 'Java'}
#{'first': 'true', 'pn': 1, 'kd': 'Python'}
]
for d in datas:
urls_queue.put(d)
for i in range(4):
t = ThreadWrite(data_queue, lock, f)
t.setDaemon(True)
t.start() urls_queue.join()
data_queue.join() with lock:
f.close()
print 'data_queue siez: %d' % data_queue.qsize()
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. centos7 mysql 5.6.30 默认配置文件

      默认配置 vim /etc/my.cnf.rpmsave mysql  Ver 14.14 Distrib 5.6.30, for linux-glibc2.5 (x86_64) using  E ...

  2. C —— 零碎笔记

    1.字节对齐和结构体大小 链接 2.共同体union 的作用 链接 3.文件夹和文件操作 windows: http://blog.csdn.net/gneveek/article/details/6 ...

  3. oracle REPLACE 函数 介绍

    oracle REPLACE 函数是用另外一个值来替代串中的某个值. 例如,可以用一个匹配数字来替代字母的每一次出现.REPLACE 的格式如下所示: REPLACE ( char, search_s ...

  4. [IOS开发进阶与实战]第一天:CoreData的运行机制

    1.数据模型NSManagedObjectModel的建立 1.- (NSManagedObjectModel *)managedObjectModel { if (_managedObjectMod ...

  5. IIS7.5 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。=

    本文转载:http://www.cnblogs.com/ctcx/archive/2012/07/19/2599741.html 在 Microsoft Visual Studio 2010 打开看了 ...

  6. Linux进程通信之System V共享内存

    前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...

  7. android uri , file , string 互转

    1:android Uri 介绍 http://www.cnblogs.com/lingyun1120/archive/2012/04/18/2455212.html 2:File 转成Uri < ...

  8. C#系列之值类型和引用类型

    前言 这几天一直在思考这章讨论什么, 在上一章讨论string的时候牵涉到引用类型,那么我们这一章讨论讨论一下,值类型和引用类型. 值类型和引用类型,它们的区别来源于传值方式.有人会认为值类型就存在栈 ...

  9. Qt Creator 你必须要掌握的快捷操作

    多使用快捷键能显著提高工作效率,尽可能减少键盘,鼠标之间切换所浪费的时间.我这里列出个人认为非常重要必须掌握的 Qt Creator 快捷键.看你知道几个? . 1 .Ctrl(按住)+ Tab快速切 ...

  10. 四种数据持久化方式(下) :SQLite3 和 Core Data

    在上文,我们介绍了iOS开发中的其中2种数据持久化方式:属性列表.归档解档. 本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运用: 在本节,将通过对4个文 ...