本文檔對平常學習中用 python 作數據爬取時所遇到的一些問題作簡要記錄,以便往後查閱,部分問題可能由於認識不到位會存在一些誤解,敬請告知,萬分感謝,共同進步。javascript
該小節主要針對於整站爬取的狀況。
爬取整站以前,確定是要先對一個網站的規模進行估計。這是可使用google搜索查看大概有多少個網址,這裏使用到google搜索的一個小技巧。html
site:url地址
有的時候可查看網站的sitemap.xml,但它有時候會過時或者滯後,不是很準確。html5
要爬的網站使用的技術會影響到咱們所寫的代碼中的處理手段。
推薦使用builtwith這個第三方包,可使用pip來安裝。
簡單使用以下:java
In [1]: import builtwith In [2]: builtwith.parse('http://example.webscraping.com/') Out[2]: {u'javascript-frameworks': [u'jQuery', u'Modernizr', u'jQuery UI'], u'programming-languages': [u'Python'], u'web-frameworks': [u'Web2py', u'Twitter Bootstrap'], u'web-servers': [u'Nginx']}
UA,即 User-Agent,是Http協議的一部分,屬於頭域的組成部分,發送 http 請求時,請求頭中會有 User-Agent字段。服務器經過該字段來分辨發送請求的瀏覽器類型、版本、內核以及操做系統信息等。
在瀏覽器 console 可用以下命令來得到該瀏覽器的 UA 標識python
navigator.userAgent
部分網站不想被爬蟲爬取就會檢查 http 請求頭的該字段內容, 因此在用爬蟲作抓取時,一般要在請求頭加上該字段,以把本身假裝成瀏覽器。有時候經過手機瀏覽器訪問獲得的頁面會更加簡潔,更容易抓取,因此假裝成手機瀏覽器也是一種好方法。
網上有不少整理的不一樣瀏覽器的 UA ,好比各類瀏覽器UserAgent一覽表。
瀏覽器野史 UserAgent列傳(上)和 瀏覽器野史 UserAgent列傳(下),這兩篇文章細說了 UA 的前因後果,去感覺下當時波瀾壯闊的「瀏覽器之戰」。git
部分服務器會檢查 http 請求頭的 Referer 字段來判斷你是不是從指定頁面跳轉而來的,以達到防盜鏈的做用。所以在假裝請求頭部的時候,該字段也是不容忽視的。github
咱們發現,URL 中有時候存在中文,這是就須要對 url 進行編碼。
能夠先將中文轉換成 utf-8 編碼,而後使用 urllib2.quote 方法對參數進行 url 編碼後傳遞。web
import urllib param = u'你好' param = param.encode('utf-8') param = urllib.quote(param)
對於 url 來講,之因此要進行編碼,是由於 url 中有些字符會引發歧義。
同理使用 unquote 能夠解碼。json
常常會遇到這樣狀況:將網頁拖到底部會自動往下加載新的數據,或者有加載更多這樣的按鈕,這裏說的就是 AJAX 了。api
AJAX 是 Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)的縮寫。它經過使用原有的 web 標準組件,實現了在不從新加載整個頁面的狀況下,與服務器進行數據交互。例如在新浪微博中,你能夠展開一條微博的評論,而不須要從新加載,或者打開一個新的頁面。可是這些內容並非一開始就在頁面中的(這樣頁面就太大了),而是在你點擊的時候被加載進來的。這就致使了你抓取這個頁面的時候,並不能得到這些評論信息(由於你沒有『展開』)。AJAX 通常是經過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 通常被縮寫爲 XHR。
這些 js 的名字一般看起來與其餘的不太同樣。
拿澎湃網舉個栗子,這纔是發送請求得到文章的真正地址,
AJAX 的一種常見用法是使用 AJAX 加載 JSON 數據,而後在瀏覽器端渲染。
這種狀況很好處理,由於 python 自帶的處理 json 的庫,舉個栗子:
豌豆莢安卓遊戲排行榜,每次點擊更多會加載新的數據。在審查元素裏,能夠看到每次點擊查看更多,都會返回一個包含應用數據詳細信息的數據。
查看請求 json 數據的地址
http://apps.wandoujia.com/api...
max 參數的值表示這一次請求返回多少個 app 信息, start 參數的值表示從第幾個 app 開始,start 從 0 開始。
另外,在構建請求頭時,應該加上這個參數 'X-Requested-With': 'XMLHttpRequest',當使用 XHR 發送 AJAX 請求時 Header 會帶上這個字段,常被用於判斷是否是 AJAX 請求。
Selenium 是一個強大的網絡數據採集工具,最初是爲網站自動測試而開發的。它可讓瀏覽器自動加載頁面,獲取所須要的數據,甚至頁面截屏,或者判斷網站上某些動做事都發生。
對應的 python 庫,能夠用 pip 安裝。
PhantomJS 是一個 headless browser,它會把網站加載到內存並執行頁面上的 JavaScript,可是不會向用戶展現網頁的圖形界面。它不是python庫,須要單獨下載(喏,你要的官網)。
寫一個簡單的栗子:
from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = webdriver.PhantomJS(executable_path='<Path to PhantomJs>') driver.get("http://www.python.org") assert "Python" in driver.title elem = driver.find_element_by_name("q") elem.clear() elem.send_keys("pycon") elem.send_keys(Keys.RETURN) assert "No results found." not in driver.page_source driver.close()
Path to PhantomJs 處 須要指定位置,若是該路徑已經加入到了環境變量中,那麼能夠不加這個參數。
這裏只是個簡單的介紹,後續會對 Selenium 的 python 版 API 的使用作單獨介紹。
部分網站對 ip 進行了限制,致使咱們沒法爬到想要的數據,這個時候能夠用代理來作。
使用 requests 這個第三方庫,能夠輕鬆地設置代理。
再舉個栗子:
import requests proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } requests.get('http://example.org', proxies=proxies)
雖然自帶的 urllib 和 urllib2 庫能夠知足需求,可是不推薦使用。爲何?由於它們的操做太繁瑣,尤爲在處理一些複雜狀況時,這不符合 python 的設計哲學,因此放手拋棄它們吧。
推薦使用 requests這個第三方庫,正如它標榜的那樣--Requests: HTTP for Humans,同時也支持 py3。
使用 requests 庫發送請求是如此的優雅,
import requests r = requests.get('https://api.github.com/events') print r.text
具體使用方法能夠看官方 API。
既然獲取到了請求頁面的源碼,那麼接下來要作的就是解析工做,通常來講,有下面三個庫用得是最多的:lxml庫, bs4庫,以及正則。
lxml 解析速度要比 bs4 快,聽說快好幾倍,正則是個終結技,只是寫起來有點麻煩。另外, bs4 不支持 xpath,而lxml 支持,總之,視本身的狀況選擇了。
前幾天還接觸過一個庫,名字叫 pyquery,它是 jQuery 的 python 實現,能夠用於解析 html 網頁內容,熟悉 jQuery 語法童鞋的福音。
前幾天遇到一個問題,問題是這樣的,html 頁面的數據通過Beautiful Soup庫的解析後,部分html源碼丟失,找不到想要的數據了,問題代碼以下:
#! /usr/bin/env python # -*- coding:utf-8 -*- import requests from bs4 import BeautifulSoup url = 'http://product.pconline.com.cn/mobile/' res = requests.get(url) html = res.text # print html soup = BeautifulSoup(html, 'lxml') site = soup.findAll('img', class_='pic') print site
輸出結果爲空,沒有想要的數據。查看官方文檔,bs 庫支持的解析庫有 lxml, html5lib 和 html.parser。用的最多的是 lxml,由於它的解析速度快,而且容錯能力強,默認也是使用該解析器。
出現解析後源碼丟失的可能緣由有 2 個:
這裏換一個解析器,換成 html.parser 就能夠了。
將編碼設置爲 utf-8
import sys reload(sys) sys.setdefaultencoding('utf-8')
總之,py2 中的編碼問題很煩人,只要解碼與編碼不一致就會出現亂碼。對 unicode 能夠 編碼,其餘編碼 decode 成 unicode。
要注意'hello' 和 u'hello' 的區別。