Python爬蟲學習筆記(二)


爬蟲接觸了也有段時間,跟着網上的一些教程,不僅做出了一些實用的小工具,而且對於使用Python爬蟲的整個流程有了大致的了解,也知道了爬蟲是怎么回事。以前做的一些小的試驗,陸續也都會寫成博客,今天記錄的, 是我在慕課網上(http://www.imooc.com/learn/563)學到的一個爬蟲框架,結構清晰合理,很值得學習,這里實現的只是爬蟲最簡單的功能,不涉及用戶的登陸和Cookie驗證,當然,這些都是可以擴充到這個框架里的。

首先,先講一講整個爬蟲框架的思路。

先確定任務:就是以百度百科Python詞條為根,陸續爬下相關的1000個網頁,這個閾值自然是可以調整的。都知道,在百度百科頁面,很多關鍵詞都是有鏈接的,鏈接到另一個網頁上,可以搜索到更多的鏈接,就這樣循環爬取,直到1000個為止。

代碼大概用了150多行,寫成了五個對象,SpiderMain、UrlManger、HtmlDownloader、HtmlParser和HtmlOutputer。SpiderMain是所有其余對象的入口,UrlManger負責管理url,用了兩個set(),一新一舊,因為已經使用過的鏈接就沒有必要再爬一次了,就放在old里,從網頁中解析出來的新的鏈接放在new里。HtmlDownloder負責將網站的數據下載到本地,HtmlParser負責把下載到本地的網頁數據里的相關數據解析出來,這里解析的,一個是鏈接,另一個是詞條的summary,HtmlOupter就負責最后結果的輸出。整個框架對應的是一個線性的流程,結構清晰。

下面看代碼。

# 實例:爬取python相關一千個網址

 

# 先導入有關的庫,這一步很重要

import urllib2

from bs4 import BeautifulSoup # 很好用的解析器

import re # 很容易遺漏,即使漏掉程序也不報錯,很隱蔽

 

# 程序的入口類

class SpiderMain(object):

# 初始化對象

def __init__(self):

self.urls = UrlManger()

self.downloader = HtmlDownloader()

self.parser = HtmlParser()

self.outputer = HtmlOutputer()

 

# 最核心的方法

def craw(self, root_url):

count = 1 # 方便計數

self.urls.add_new_url(root_url)

while self.urls.has_new_url():

try:

# 取出url

new_url = self.urls.get_new_url()

print 'craw %d : %s' % (count, new_url)

# 根據url下載網頁內容

html_cont = self.downloader.download(new_url)

# 將網頁內容解析為需要的數據

new_urls, new_data = self.parser.parse(new_url, html_cont)

# 將數據各自存放

self.urls.add_new_urls(new_urls)

self.outputer.collect_data(new_data)

 

if count == 10:

break

 

count += 1

 

except:

print 'craw failed'

 

# 將數據輸出為html文件

self.outputer.output_html()

 

 

 

class UrlManger(object):

 

# 初始化,兩個set(),因為set()里的數據不會重復

def __init__(self):

self.new_urls = set()

self.old_urls = set()

 

# 添加一個url

def add_new_url(self, url):

if url is None:

return

if url not in self.new_urls and url not in self.old_urls:

self.new_urls.add(url)

 

# 添加多個url,循環使用add_new_url,很巧妙

def add_new_urls(self, urls):

if urls is None or len(urls) == 0:

return

for url in urls:

self.add_new_url(url)

 

# 判斷是否還有沒有爬取的url,返回bool值

def has_new_url(self):

return len(self.new_urls) != 0

 

# 獲取一個url

def get_new_url(self):

new_url = self.new_urls.pop() # pop用的極好,不僅取得了數據,而且將元素從set()中刪除

self.old_urls.add(new_url) # 使用過的url要放在old_urls里,避免重復爬取

return new_url

 

# 下載器

class HtmlDownloader(object):

 

def download(self, url):

if url is None:

return None

 

# Python自帶的urllib2下載模塊

response = urllib2.urlopen(url)

 

# 可以用getcode()方法判斷是否下載成功

if response.getcode() != 200:

return None

 

return response.read()

 

# Html解析器,核心,倒着看

class HtmlParser(object):

 

def get_new_urls(self, soup):

 

# 解析出來的url也放在一個set()里

new_urls = set()

# 正則表達式是根據網頁源代碼分析出來的

links = soup.find_all('a', href=re.compile(r'/view/\d+\.htm'))

for link in links:

new_url = link['href']

# 解析出來的鏈接是個相對url,要合成成一個絕對url

page_url = 'http://baike.baidu.com'

new_full_url = page_url + new_url

new_urls.add(new_full_url)

return new_urls

 

def get_new_data(self, page_url, soup):

 

# 先構造一個字典

res_data = {}

 

# url

res_data['url'] = page_url

 

# <dd class="lemmaWgt-lemmaTitle-title"><h1>Python</h1>

title_node = soup.find('dd', class_='lemmaWgt-lemmaTitle-title').find('h1')

res_data['title'] = title_node.get_text()

 

# <div class="lemma-summary" label-module="lemmaSummary">

summary_node = soup.find('div', class_='lemma-summary')

res_data['summary'] = summary_node.get_text()

 

return res_data

 

def parse(self, page_url, html_cont):

 

if page_url is None or html_cont is None:

return

 

# BeautifulSoup固定用法,先構造一個BeautifulSoup對象

soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')

 

# 方法里面嵌套方法,值得學習

new_urls = self.get_new_urls(soup)

new_data = self.get_new_data(page_url, soup)

return new_urls, new_data

 

# 輸出器

class HtmlOutputer(object):

 

def __init__(self):

self.datas = []

 

def collect_data(self, data):

if data is None:

return

self.datas.append(data)

 

# 輸出為html文件

def output_html(self):

# 以寫的形式打開文件

fout = open('D:/Learn/Code/python/pachong/output.html', 'w')

 

# 這一句必須加上,否則中文字符會顯示亂碼

fout.write('<meta charset=utf-8" />')

fout.write('<html>')

fout.write('<body>')

fout.write('<table>')

fout.write('<th>URL</th>')

fout.write('<th>Title</th>')

fout.write('<th>Summary</th>')

 

for data in self.datas:

fout.write('<tr>')

fout.write('<td>%s</td>' % data['url'])

# 注意中文字符的編碼

fout.write('<td>%s</td>' % data['title'].encode('utf-8'))

fout.write('<td>%s</td>' % data['summary'].encode('utf-8'))

fout.write('</tr>')

 

fout.write('</table>')

fout.write('</body>')

fout.write('</html>')

 

# 不要忘記關閉文件

fout.close()

 

# 初始化程序

root_url = 'http://baike.baidu.com/view/21087.htm'

obj_spider = SpiderMain()

obj_spider.craw(root_url)

結果如下:

打開output.html

沒什么問題!

 

總結:雖然名為一個簡易的爬蟲框架,其實已經不簡單,難得的是思路如此清晰,結構很嚴謹,很多細節都很值得玩味。就憑這一個項目,也能看得出自己與高手之間的差距。從這些代碼中,我領悟到,在寫代碼之前,一定要先理清思路;其次,這里用到了面向對象(oop)的方式,功能各自獨立,又互相補充,這不就是高內聚、低耦合嗎?要學的還有很多!

 

源代碼地址:https://github.com/Lucifer25/Learn-Python/blob/master/Web-Crawler/SimpleFramework.py

 

參考資料:http://www.imooc.com/learn/563


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM