繼續上次的話題,這次抓取的網頁是天涯論壇中,“地緣看世界”
1、擷取網址:通過Regex來擷取各貼子網址
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的方法,但這種方法速度很慢,也可採用Regex的方法,但這樣適應性不強。文本中也以
這個貼子中有大量的圖片,在<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在一起使用感到困惑。
多線程下載還是存在很多問題,各位在借鑒時一定要注意。