文章目录
  1. 1. 一、封装pycurl
  2. 2. 二、实现多线程采集
  3. 3. 三、最终重写的采集网站信息的代码

昨天写的那个采集网站的代码太次了,太容易出问题了。不过就这么几行代码也就不追求这些健壮性了。现在最头痛的写代码经常都是一次性的,过期做废,几乎不可重用。重复造轮子的代码太多了,所以把采集的这个东西整理了一下,重新写了一遍,把一些重要的东西封装了起来。

对于采集这一块的代码参考了 http://obmem.info/?p=753 里面的多线程抓取,对里面的Fetcher类进行了一下改写,把urllib2换成了pycurl。据说pycurl的性能比urllib2要好,反正我也没测试过。用起来的感觉还不错。

一、封装pycurl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#coding:utf-8
import pycurl,StringIO

class Tpycurl():
low_speed_time = 100
connection_timeout = 100
timeout = 100
def __init__(self,url=None):
self.content = StringIO.StringIO()
self.curl_handle = pycurl.Curl()
if url != None:
self.curl_handle.setopt(pycurl.URL,url)
self.curl_handle.setopt(pycurl.FOLLOWLOCATION, 1)
self.curl_handle.setopt(pycurl.MAXREDIRS, 5)
self.curl_handle.setopt(pycurl.CONNECTTIMEOUT, self.connection_timeout)
self.curl_handle.setopt(pycurl.TIMEOUT, self.timeout)
self.curl_handle.setopt(pycurl.NOSIGNAL, 1)
self.curl_handle.setopt(pycurl.LOW_SPEED_LIMIT, 100)
self.curl_handle.setopt(pycurl.LOW_SPEED_TIME, self.low_speed_time)
self.curl_handle.setopt(pycurl.HTTPHEADER, ["User-Agent: %s"%"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)"])
#self.curl_handle.setopt(pycurl.MAXFILESIZE, max_size)
self.curl_handle.setopt(pycurl.COOKIEFILE, 'cookies.txt')
self.curl_handle.setopt(pycurl.COOKIEJAR, 'cookies.txt')
self.curl_handle.setopt(pycurl.WRITEFUNCTION, self.content.write)

def open(self,url):
self.curl_handle.setopt(pycurl.URL,url)

def read(self):
try:
self.curl_handle.perform()
return (self.curl_handle.getinfo(pycurl.HTTP_CODE),self.content.getvalue(), self.curl_handle.getinfo(pycurl.CONTENT_TYPE))
except pycurl.error,e:
print e
return (-1,e,0)

目前就是简单的封装一下,方便调用而已,没详细研究过其中的参数。

二、实现多线程采集

修改了一下observer大神的Fetcher类,改成pycurl的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#coding:utf-8
from Tpycurl import Tpycurl
from threading import Thread,Lock
from Queue import Queue
import time

class Fetcher:
retries = 3
def __init__(self,threads):
self.lock = Lock() #线程锁
self.q_req = Queue() #任务队列
self.q_ans = Queue() #完成队列
self.threads = threads
for i in range(threads):
t = Thread(target=self.threadget)
t.setDaemon(True)
t.start()
self.running = 0

def __del__(self): #解构时需等待两个队列完成
time.sleep(0.5)
self.q_req.join()
self.q_ans.join()

def taskleft(self):
return self.q_req.qsize()+self.q_ans.qsize()+self.running

def push(self,req):
self.q_req.put(req)

def pop(self):
return self.q_ans.get()

def threadget(self):
self.opener = Tpycurl()
while True:
req = self.q_req.get()
with self.lock: #要保证该操作的原子性,进入critical area
self.running += 1
retries = self.retries
while retries>0:
ans = Tpycurl(req).read()
if ans[0] == 200:
ans = ans[1]
break
elif retries <= 0:
ans = ''
print 'GET FAILED',req
retries+=-1

self.q_ans.put((req,ans))
with self.lock:
self.running -= 1
self.q_req.task_done()
time.sleep(0.1) # don't spam

有了这个类代码写起来舒服多了,早就应该这么干了。以后多研究研究写轮子,搞成通用性的。省得以后再写这么多乱七八糟的代码。

三、最终重写的采集网站信息的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#coding:utf-8

from lxml import html
from utils.TFetcher import Fetcher
import re,sys


links=[]#存放下载的页面地址
for i in range(132):
if i==0:
links.append('http://www.hltm.cc/list/18.html')
else:
links.append('http://www.hltm.cc/list/18_%s.html'%(i+1))

f = Fetcher(10)#开启10线程

for url in links:
f.push(url)#将地址放入队列


def GetWorks(content):
dom = html.document_fromstring(content)#生成html的xml对象
works = []
for work in dom.xpath('//div[@class="listInfo"]'):
title = work.xpath('./h3/a')[0].text#取出标题
downloads = int(re.findall('.*?(\d+)',work.xpath('./p')[0].text)[0])#取出下载量,并转化成数字
works.append((title,downloads))#添加到works
return works

works = []
while f.taskleft():
url,content = f.pop()
content = content.decode('gbk')
works += GetWorks(content)#获取信息并添加到列表
print u"下载:%s成功"%url

def mcmp(x,y):
if x[1]&gt;y[1]:
return 1
elif x[1]==y[1]:
return 0
else:
return -1

works.sort(mcmp,reverse=True)
worksort = open('worksort.txt',"w+")
sys.stdout = worksort
for work in works:
print (u'作品名:%s 下载次数:%s'%(work[0],work[1])).encode('utf8')
worksort.close()

代码比昨天写的要清楚一点,以前太少用到类的特性,封装成类后,代码调用还真方便,扩展也方便。而且重写的时候发现,原来list是可以直接用加号的,上次还用循环来做,真是太傻了。

文章目录
  1. 1. 一、封装pycurl
  2. 2. 二、实现多线程采集
  3. 3. 三、最终重写的采集网站信息的代码