Ⅰ 如何用最簡單的Python爬蟲採集整個網站
在之前的文章中Python實現「維基網路六度分隔理論「之基礎爬蟲,我們實現了在一個網站上隨機地從一個鏈接到另一個鏈接,但是,如果我們需要系統地把整個網站按目錄分類,或者要搜索網站上的每一個頁面,我們該怎麼辦?我們需要採集整個網站,但是那是一種非常耗費內存資源的過程,尤其是處理大型網站時,比較合適的工具就是用一個資料庫來存儲採集的資源,之前也說過。下面來說一下怎麼做。
網站地圖sitemap
網站地圖,又稱站點地圖,它就是一個頁面,上面放置了網站上需要搜索引擎抓取的所有頁面的鏈接(註:不是所有頁面,一般來說是所有文章鏈接。大多數人在網站上找不到自己所需要的信息時,可能會將網站地圖作為一種補救措施。搜索引擎蜘蛛非常喜歡網站地圖。
對於SEO,網站地圖的好處:
1.為搜索引擎蜘蛛提供可以瀏覽整個網站的鏈接簡單的體現出網站的整體框架出來給搜索引擎看;
2.為搜索引擎蜘蛛提供一些鏈接,指向動態頁面或者採用其他方法比較難以到達的頁面;
3.作為一種潛在的著陸頁面,可以為搜索流量進行優化;
4.如果訪問者試圖訪問網站所在域內並不存在的URL,那麼這個訪問者就會被轉到「無法找到文件」的錯誤頁面,而網站地圖可以作為該頁面的「准」內容。
數據採集
採集網站數據並不難,但是需要爬蟲有足夠的深度。我們創建一個爬蟲,遞歸地遍歷每個網站,只收集那些網站頁面上的數據。一般的比較費時間的網站採集方法從頂級頁面開始(一般是網站主頁),然後搜索頁面上的所有鏈接,形成列表,再去採集到的這些鏈接頁面,繼續採集每個頁面的鏈接形成新的列表,重復執行。
很明顯,這是一個復雜度增長很快的過程。加入每個頁面有10個鏈接,網站上有5個頁面深度,如果採集整個網站,一共得採集的網頁數量是105,即100000個頁面。
因為網站的內鏈有很多都是重復的,所以為了避免重復採集,必須鏈接去重,在Python中,去重最常用的方法就是使用自帶的set集合方法。只有「新」鏈接才會被採集。看一下代碼實例:
from urllib.request import urlopenfrom bs4 import BeautifulSoupimport repages = set()def getLinks(pageurl):globalpageshtml= urlopen("" + pageurl)soup= BeautifulSoup(html)forlink in soup.findAll("a", href=re.compile("^(/wiki/)")):if'href' in link.attrs:iflink.attrs['href'] not in pages:#這是新頁面newPage= link.attrs['href']print(newPage)pages.add(newPage)getLinks(newPage)getLinks("")
原理說明:程序執行時,用函數處理一個空URL,其實就是維基網路的主頁,然後遍歷首頁上每個鏈接,並檢查是否已經在全局變數集合pages裡面,如果不在,就列印並添加到pages集合,然後遞歸處理這個鏈接。
遞歸警告:Python默認的遞歸限制是1000次,因為維基網路的鏈接浩如煙海,所以這個程序達到遞歸限制後就會停止。如果你不想讓它停止,你可以設置一個遞歸計數器或者其他方法。
採集整個網站數據
為了有效使用爬蟲,在用爬蟲的時候我們需要在頁面上做一些事情。我們來創建一個爬蟲來收集頁面標題、正文的第一個段落,以及編輯頁面的鏈接(如果有的話)這些信息。
第一步,我們需要先觀察網站上的頁面,然後制定採集模式,通過F12(一般情況下)審查元素,即可看到頁面組成。
觀察維基網路頁面,包括詞條和非詞條頁面,比如隱私策略之類的頁面,可以得出下面的規則:
所有的標題都是在h1→span標簽里,而且頁面上只有一個h1標簽。
所有的正文文字都在div#bodyContent標簽里,如果我們想獲取第一段文字,可以用div#mw-content-text→p,除了文件頁面,這個規則對所有頁面都適用。
編輯鏈接只出現在詞條頁面上,如果有編輯鏈接,都位於li#ca-edit標簽的li#ca-edit→span→a裡面。
調整一下之前的代碼,我們可以建立一個爬蟲和數據採集的組合程序,代碼如下:
import redef getLinks(pageUrl):global pageshtml = urlopen("" + pageUrl)soup = BeautifulSoup(html)try:print(soup.h1.get_text())print(soup.find(id="mw-content-text").findAll("p")[0])print(soup.find(id="ca-edit").find("span").find("a").attrs['href'])except AttributeError:print("頁面缺少屬性")for link in soup.findAll("a", href =re.compile("^(/wiki/)")):if 'href' in link.attrs:#這是新頁面newPage = link.attrs['href']print("------------------\n"+newPage)
這個for循環和原來的採集程序基本上是一樣的,因為不能確定每一頁上都有所有類型的數據,所以每個列印語句都是按照數據在頁面上出現的可能性從高到低排列的。
數據存儲到MySQL
前面已經獲取了數據,直接列印出來,查看比較麻煩,所以我們就直接存到MySQL裡面吧,這里只存鏈接沒有意義,所以我們就存儲頁面的標題和內容。前面我有兩篇文章已經介紹過如何存儲數據到MySQL,數據表是pages,這里直接給出代碼:
import reimport datetimeimport randomimport pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306, user = 'root', passwd = '19930319', db = 'wiki', charset ='utf8mb4')cur = conn.cursor()cur.execute("USE wiki")#隨機數種子random.seed(datetime.datetime.now())#數據存儲def store(title, content):cur.execute("INSERT INTO pages(title, content)VALUES(\"%s\", \"%s\")", (title, content))cur.connection.commit()def getLinks(articleUrl):html = urlopen("" + articleUrl)title = soup.find("h1").get_text()content =soup.find("div",{"id":"mw-content-text"}).find("p").get_text()store(title, content)returnsoup.find("div",{"id":"bodyContent"}).findAll("a",href=re.compile("^(/wiki/)((?!:).)*$"))#設置第一頁links =getLinks("/wiki/Kevin_Bacon")try:while len(links)>0:newArticle = links[random.randint(0, len(links)-1)].attrs['href']print (newArticle)links = getLinks(newArticle)finally:cur.close()conn.close()
小結
今天主要講一下Python中遍歷採集一個網站的鏈接,方便下面的學習。
希望通過上面的操作能幫助大家。如果你有什麼好的意見,建議,或者有不同的看法,我都希望你留言和我們進行交流、討論。
Ⅱ 如何用python寫爬蟲來獲取網頁中所有的文章以及關鍵詞
所謂網頁抓取,就是把URL地址中指定的網路資源從網路流中讀取出來,保存到本地。
類似於使用程序模擬IE瀏覽器的功能,把URL作為HTTP請求的內容發送到伺服器端, 然後讀取伺服器端的響應資源。
在Python中,我們使用urllib2這個組件來抓取網頁。
urllib2是Python的一個獲取URLs(Uniform Resource Locators)的組件。
它以urlopen函數的形式提供了一個非常簡單的介面。
最簡單的urllib2的應用代碼只需要四行。
我們新建一個文件urllib2_test01.py來感受一下urllib2的作用:
import urllib2
response = urllib2.urlopen('http://www..com/')
html = response.read()
print html
按下F5可以看到運行的結果:
我們可以打開網路主頁,右擊,選擇查看源代碼(火狐OR谷歌瀏覽器均可),會發現也是完全一樣的內容。
也就是說,上面這四行代碼將我們訪問網路時瀏覽器收到的代碼們全部列印了出來。
這就是一個最簡單的urllib2的例子。
除了"http:",URL同樣可以使用"ftp:","file:"等等來替代。
HTTP是基於請求和應答機制的:
客戶端提出請求,服務端提供應答。
urllib2用一個Request對象來映射你提出的HTTP請求。
在它最簡單的使用形式中你將用你要請求的地址創建一個Request對象,
通過調用urlopen並傳入Request對象,將返回一個相關請求response對象,
這個應答對象如同一個文件對象,所以你可以在Response中調用.read()。
我們新建一個文件urllib2_test02.py來感受一下:
import urllib2
req = urllib2.Request('http://www..com')
response = urllib2.urlopen(req)
the_page = response.read()
print the_page
可以看到輸出的內容和test01是一樣的。
urllib2使用相同的介面處理所有的URL頭。例如你可以像下面那樣創建一個ftp請求。
req = urllib2.Request('ftp://example.com/')
在HTTP請求時,允許你做額外的兩件事。
1.發送data表單數據
這個內容相信做過Web端的都不會陌生,
有時候你希望發送一些數據到URL(通常URL與CGI[通用網關介面]腳本,或其他WEB應用程序掛接)。
在HTTP中,這個經常使用熟知的POST請求發送。
這個通常在你提交一個HTML表單時由你的瀏覽器來做。
並不是所有的POSTs都來源於表單,你能夠使用POST提交任意的數據到你自己的程序。
一般的HTML表單,data需要編碼成標准形式。然後做為data參數傳到Request對象。
編碼工作使用urllib的函數而非urllib2。
我們新建一個文件urllib2_test03.py來感受一下:
import urllib
import urllib2
url = 'http://www.someserver.com/register.cgi'
values = {'name' : 'WHY',
'location' : 'SDU',
'language' : 'Python' }
data = urllib.urlencode(values) # 編碼工作
req = urllib2.Request(url, data) # 發送請求同時傳data表單
response = urllib2.urlopen(req) #接受反饋的信息
the_page = response.read() #讀取反饋的內容
如果沒有傳送data參數,urllib2使用GET方式的請求。
GET和POST請求的不同之處是POST請求通常有"副作用",
它們會由於某種途徑改變系統狀態(例如提交成堆垃圾到你的門口)。
Data同樣可以通過在Get請求的URL本身上面編碼來傳送。
import urllib2
import urllib
data = {}
data['name'] = 'WHY'
data['location'] = 'SDU'
data['language'] = 'Python'
url_values = urllib.urlencode(data)
print url_values
name=Somebody+Here&language=Python&location=Northampton
url = 'http://www.example.com/example.cgi'
full_url = url + '?' + url_values
data = urllib2.open(full_url)
這樣就實現了Data數據的Get傳送。
2.設置Headers到http請求
有一些站點不喜歡被程序(非人為訪問)訪問,或者發送不同版本的內容到不同的瀏覽器。
默認的urllib2把自己作為「Python-urllib/x.y」(x和y是Python主版本和次版本號,例如Python-urllib/2.7),
這個身份可能會讓站點迷惑,或者乾脆不工作。
瀏覽器確認自己身份是通過User-Agent頭,當你創建了一個請求對象,你可以給他一個包含頭數據的字典。
下面的例子發送跟上面一樣的內容,但把自身模擬成Internet Explorer。
(多謝大家的提醒,現在這個Demo已經不可用了,不過原理還是那樣的)。
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {'name' : 'WHY',
'location' : 'SDU',
'language' : 'Python' }
headers = { 'User-Agent' : user_agent }
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
the_page = response.read()
以上就是python利用urllib2通過指定的URL抓取網頁內容的全部內容,非常簡單吧,希望對大家能有所幫助。