Python分散式爬蟲前菜(1):關於靜態動態網頁內容獲取的N種方法
爬蟲是我們快速獲取需要的資料的一個非常有效的途徑,而爬蟲的第一步便是能夠請求遠方伺服器為我們返回所需的網頁資訊。我們知道,正常情況下在瀏覽器上我們只要輸入正確的統一資源定位器url,即網頁地址便可輕鬆開啟我們想要看到頁面。同理,在設計python爬蟲程式時,我們也可以呼叫對應的庫通過引數設定來連線網路處理http協議。針對靜態的網頁,常常被使用到的庫有urllib, urllib2, requests等等,通過它們可以很方便的請求伺服器返回特定地址的網頁內容。但是如果遇到JS載入的動態網頁,用前面的方法往往收不到我們想要的結果。這時候就可以召喚出強大的自動化測試工具Selenium並喊上它的小夥伴PhantomJS來一起升級打怪了。
(一) urllib, urllib2,直接上例子:
import urllib2
response = urllib2.urlopen("http://www.baidu.com")
print response.read()
上面只要給一個url,例如百度,呼叫urllib2庫便可以單槍匹馬讀到這個網址對應的網頁原始碼,程式碼特別簡潔。實際爬蟲中由於考慮到對方的反爬蟲機制,網路響應時間或者傳送請求需要新增額外的資訊 ,我們需要多新增幾行程式碼,目的是讓伺服器儘量相信收到的請求都是來自正常的訪問物件。為了程式邏輯清晰,我們可以為urlopen設計一個request物件作為傳入引數,例如:
import urllib import urllib2 #新增url url = 'xxx' request = urllib2.Request(url) #為了模擬瀏覽器行為,偽裝對方識別問題,可以新增Headers屬性,例如下面的agent便是設定請求身份: user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36' headers = { 'User-Agent' : user_agent} request = urllib2.Request(url, headers) #有時訪問某些網站需要提供一些資訊,例如使用者名稱和密碼,這時可以這樣: values = {"username":"yourname","password":"????"} data = urllib.urlencode(values) request = urllib2.Request(url, data, headers) #在遇到網路狀況不好時可以設定timeout來設定等待多久超時 response = urllib2.urlopen(request,timeout=18) print response.read()
(二)requests,一款簡單美觀而友好的外部庫。
requests所有的功能都可以通過以下7個方法訪問。它們全部都會返回一個response物件的例項。
更詳細的介紹可以參考官網requests。#建立併發送一個request requests.request(method, url, **kwargs) 引數: method -- method for the new Request object. url -- URL for the new Request object. params -- (optional) Dictionary or bytes to be sent in the query string for the Request. data -- (optional) Dictionary, bytes, or file-like object to send in the body of the Request. json -- (optional) json data to send in the body of the Request. headers -- (optional) Dictionary of HTTP Headers to send with the Request. cookies -- (optional) Dict or CookieJar object to send with the Request. files -- (optional) Dictionary of 'name': file-like-objects (or {'name': file-tuple}) for multipart encoding upload. file-tuple can be a 2-tuple ('filename', fileobj), 3-tuple ('filename', fileobj, 'content_type') or a 4-tuple ('filename', fileobj, 'content_type', custom_headers), where 'content-type' is a string defining the content type of the given file and custom_headers a dict-like object containing additional headers to add for the file. auth -- (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. timeout (float or tuple) -- (optional) How long to wait for the server to send data before giving up, as a float, or a (connect timeout, read timeout) tuple. allow_redirects (bool) -- (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. proxies -- (optional) Dictionary mapping protocol to the URL of the proxy. verify -- (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to True. stream -- (optional) if False, the response content will be immediately downloaded. cert -- (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. #例如: import requests url='xxxx' headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'} proxies = { 'http' : '127.0.0.1:8118'} response=requests.request('GET',url, timeout=20, proxies=proxies, headers=headers) 返回型別requests.Response 另: #傳送一個HEAD request. requests.head(url, **kwargs) #傳送一個GET request requests.get(url, params=None, **kwargs) #傳送一個POST request requests.post(url, data=None, json=None, **kwargs) #傳送一個PUT request requests.put(url, data=None, **kwargs) #傳送一個PATCH request requests.patch(url, data=None, **kwargs) #傳送一個DELETE request requests.delete(url, **kwargs)
(三)Selenium + PhantomJs,高效處理動態網頁的完美組合。
我們使用前面的方法可以單純的獲取網頁的html程式碼,如若是遇到要經過JS渲染的網頁內容時則會變得非常麻煩。所以我們需要能夠像瀏覽器一樣處理要JS渲染的頁面的工具,而PhantomJs正是一款基於WebKit的無介面的網頁互動工具,其提供的JavaScript API可以實現自動化瀏覽、截圖等瀏覽器功能。Selenium是一款自動化測試工具,其支援Firefox,Chrome,Safari等主流的瀏覽器,藉助selenium就可以模擬人類的各種網頁操作行為,例如開啟瀏覽器,輸入資訊,點選,翻頁等等。PhantomJS作為一款無介面的瀏覽器,Selenium會對它感冒嗎?答案是非常感冒,因為PhantomJs不僅能完成瀏覽器的功能,其效率相對來說還更高。例如:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
phantomjs_path = '/data/opt/brew/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs'
driver = webdriver.PhantomJS(executable_path=phantomjs_path)
url = 'xxxx'
driver.get(url)
#輕鬆得到到JS渲染頁面的原始碼
page_source = driver.page_source.encode('utf8')
有時候我們需要做到頁面互動,即可以模擬人在瀏覽器上點選,輸入,滑鼠移動等各種行為時,這就需要先做到對頁面元素的定位問題。其中WebDriver提供了多種方法來實現元素定位:
#定位一個元素的方法有
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector
#定位多元素,返回一個list,方法有
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
#例如有如下網頁原始碼:
<html>
<body>
<form id="loginForm">
<input name="username" type="text" />
<input name="password" type="password" />
<input name="continue" type="submit" value="Login" />
<input name="continue" type="button" value="Clear" />
</form>
</body>
<html>
#form可以這樣來定位
login_form = driver.find_element_by_id('loginForm')
#username&password兩個元素定位如下
username = driver.find_element_by_name('username')
password = driver.find_element_by_name('password')
#如果使用xpath來定位username,以下方法都ok
username = driver.find_element_by_xpath("//form[input/@name='username']")
username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]")
username = driver.find_element_by_xpath("//input[@name='username']")
更多內容可以參考selenium_python,PhantomJS。
這篇文章簡短介紹了網頁原始碼內容獲取的各種方法,包括靜態網頁常常使用到的urllib,urllib2,requests和動態網頁使用到的selenium,phantomjs組合。在爬蟲過程中,我們往往需要提取並保留網頁中有用的資訊,所以接下來要介紹的是如何針對獲得的網頁原始碼提取出有用資訊的工作。