您的位置:首页 > 编程语言 > Python开发

Python网页抓取程序(续)

2011-08-08 22:13 585 查看
继续上次的话题,这次抓取的网页是天涯论坛中,“地缘看世界”

1、获取网址:通过正则表达式来获取各贴子网址

link='http://www.tianya.cn/publicforum/content/worldlook/1/223829.shtml'
html=urllib2.urlopen(link).read()
m=re.search(r'name=\'idArticleslist\' value=\S*>',html)
IDs=re.findall(r'[0-9]+',m.group(0))

for ID in IDs:
url="http://www.tianya.cn/publicforum/content/worldlook/1/%s.shtml"%ID

2、 下载网页:以前是边下载边处理,这样处理时间长,有时还有下载不了的情况,改为下载到指定目录,并在下载前检查是否存在同名

htmldir=r'.\html\\'

filename=htmldir+url.split('/')[-1]
if (not os.path.exists(filename)) or os.path.getsize(filename)==0:
print 'downloading'+filename+'\n'
html=urlRead(url)
if len(html)>0:
f=open(filename,'w')
f.write(html)
f.close()

3、下载后对网页内容进行分析,在分析前要对网页进行处理以去除htmlparser无法处理的部分,实质是对网页进行截取,并将无法处理字符串替换

txts=re.split(r'<div class="respond" id="adsp_content_replybox_frame_1">',html)
txt=txts[0]
txt=re.sub('\xcb\xce\xcc\xe5','\'\xcb\xce\xcc\xe5\'', txt)

4、提取贴子的正文,还是正规的htmlparser的方法,但这种方法速度很慢,也可采用正则表达式的方法,但这样适应性不强。文本中也以

这个贴子中有大量的图片,在<imgsrc=链接地址>的形式保存

class DocParser(HTMLParser.HTMLParser):
def __init__(self,pool):
self.pool=pool
self.startread=0
self.pre=0
HTMLParser.HTMLParser.__init__(self)
self.doc=''
def handle_starttag(self, tag, attrs):
if tag=='span':
for (name,value) in attrs:
if name=='value' and value=='10174465':
self.pre=1
if tag=="div" and self.pre==1:
for (name,value) in attrs:
if name=='class' and value =='post':
self.startread= 1
if tag=='img' and self.startread==1:

for (name,value) in attrs:
if name=='original':

imgname=value.split('/')[-4]+value.split('/')[-3]\
+value.split('/')[-2]+value.split('/')[-1]
self.doc+='<imgsrc=%s>\n'%imgname
if not os.path.exists(htmldir+imgname):
self.pool.add_task(getImg,value,htmldir)

def handle_endtag(self, tag):
if tag == 'div' and self.startread==1:
self.doc+='\n\n\n'
self.pre=0
self.startread = 0
def handle_data(self,data):
if self.startread:
self.doc+=data
self.doc+='\n'

5、破解防外链接:通过设置Referer实现

preurl='http://www.tianya.cn/'

req = urllib2.Request(url)

req.add_header('Referer', preurl)

6、提高urlopen工作健壮性,设置重试次数和超时等待

改造后的urlopen如下:

def urlRead(url):
fails = 0
rs=''
preurl='http://www.tianya.cn/'
while True:
try:

if fails >= 100:
print 'Failed to Read '+url
break
#设置Referer,避免防盗链
req = urllib2.Request(url)
req.add_header('Referer', preurl)
response=urllib2.urlopen(req,timeout=30)
length=response.info()['Content-Length']
rs=response.read()
if len(rs)==length:
continue

except Exception:
fails += 1
time.sleep(10)
else:
break

return rs

7、简易多线程下载:以前试用过stackless感觉没有效果,后来还是使用threadpool类,

from Queue import Queue
from threading import Thread

class Worker(Thread):
"""Thread executing tasks from a given tasks queue"""
def __init__(self, tasks):
Thread.__init__(self)
self.tasks = tasks
self.daemon = True
self.start()

def run(self):
while True:
func, args, kargs = self.tasks.get()
try: func(*args, **kargs)
except Exception, e: print e
self.tasks.task_done()

class ThreadPool:
"""Pool of threads consuming tasks from a queue"""
def __init__(self, num_threads):
self.tasks = Queue(num_threads)
for _ in range(num_threads): Worker(self.tasks)

def add_task(self, func, *args, **kargs):
"""Add a task to the queue"""
self.tasks.put(( func, args, kargs))

def wait_completion(self):
"""Wait for completion of all the tasks in the queue"""
self.tasks.join()

使用时:

pool = ThreadPool(200)

for ID in IDs:
url="http://www.tianya.cn/publicforum/content/worldlook/1/%s.shtml"%ID
pool.add_task(getHtml,url,htmldir)

pool.wait_completion()

8、输出到pdf,我使用了reportlab要注意的是:

引用字体:

reportlab.rl_config.warnOnMissingFontGlyphs = 0

pdfmetrics.registerFont(TTFont('YaHei', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('YaHeiBD', 'msyhbd.ttf'))
fonts.addMapping('YaHei', 0, 0, 'YaHei')
fonts.addMapping('YaHei', 0, 1, 'YaHei')
fonts.addMapping('YaHeiBD', 1, 0, 'YaHeiBD')
fonts.addMapping('YaHeiBD', 1, 1, 'YaHeiBD')
stylesheet=getSampleStyleSheet()
normalStyle = copy.deepcopy(stylesheet['Normal'])
normalStyle.fontName ='YaHei'

其次是中文换行:

normalStyle.wordWrap = 'CJK'

中文的左缩进有bug设置左缩进后,第一行全部向右移,并不是比其他的行前面少几个字

最后是嵌入图片:取得图片的长和宽,然后设置缩放比,

def get_image(path):
width=439
height=685
img = utils.ImageReader(path)
iw, ih = img.getSize()

if iw>width or ih>height:
rw=float(iw)/float(width)
rh=float(ih)/float(height)
if rw>rh:
return Image(path,width,round(ih/rw))
else:
return Image(path,round(iw/rh),height)
else:
return Image(path)

结论:python是处理网页的利器,但我在编码上浪费了许多时间,在htmlparser和re中对中文的编码支持也不好,不知道各达人有没有什么好方法。

python号称是“胶水语言”,但我始终对如何将其与其他诸如C#,java在一起使用感到困惑。

多线程下载还是存在很多问题,各位在借鉴时一定要注意。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: