1. 程式人生 > >爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

爬蟲原理與資料抓取

Requests簡單使用

新增 headers 和 查詢引數


                學習Python中有不明白推薦加入交流群
                號:516107834
                群裡有志同道合的小夥伴,互幫互助,
                群裡有不錯的學習教程!

如果想新增 headers,可以傳入headers引數來增加請求頭中的headers資訊。如果要將引數放在url中傳遞,可以利用 params 引數

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

import requests
kw = {'wd':'長城'}
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# params 接收一個字典或者字串的查詢引數,字典型別自動轉換為url編碼,不需要urlencode()
response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers)
# 檢視響應內容,response.text 返回的是Unicode格式的資料
print (response.text)
# 檢視響應內容,response.content返回的位元組流資料
print (respones.content)
# 檢視完整url地址
print (response.url)
# 檢視響應頭部字元編碼
print (response.encoding)
# 檢視響應碼
print (response.status_code)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

使用response.text 時,Requests 會基於 HTTP 響應的文字編碼自動解碼響應內容,大多數 Unicode 字符集都能被無縫地解碼。 使用response.content 時,返回的是伺服器響應資料的原始二進位制位元組流,可以用來儲存圖片等二進位制檔案。

requests預設自帶的Accept-Encoding導致或者新浪預設傳送的就是壓縮之後的網頁

但是為什麼content.read()沒有問題,因為requests,自帶解壓壓縮網頁的功能

當收到一個響應時,Requests 會猜測響應的編碼方式,用於在你呼叫response.text 方法時對響應進行解碼。Requests 首先在 HTTP 頭部檢測是否存在指定的編碼方式,如果不存在,則會使用 chardet.detect來嘗試猜測編碼方式(存在誤差)

更推薦使用response.content.deocde()

Requests深入

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

# 如果是json檔案可以直接顯示
print (response.json())
# unquote將url格式的中文還原
a = requests.utils.unquote('http://www.baidu.com/f?kw=%E6%9D%E6%85%')
print(a)
http://www.baidu.com/f?kw=李子
通過本地環境變數 HTTP_PROXY 和 HTTPS_PROXY 來配置代理:
export HTTP_PROXY="http://12.34.56.79:9527"
export HTTPS_PROXY="https://12.34.56.79:9527"

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 

私密代理驗證(特定格式) 和 Web客戶端驗證(auth 引數)

私密代理

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

import requests
# 如果代理需要使用HTTP Basic Auth,可以使用下面這種格式:
proxy = { "http": "mr_mao_hacker:[email protected]:16816" }
response = requests.get("http://www.baidu.com", proxies = proxy)
print (response.text)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

web客戶端驗證

如果是Web客戶端驗證,需要新增 auth = (賬戶名, 密碼)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

1 import requests
2 
3 auth=('test', '123456')
4 
5 response = requests.get('http://192.168.199.107', auth = auth)
6 
7 print (response.text)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

Cookies

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 import requests
 2 
 3 response = requests.get("http://www.baidu.com/")
 4 
 5 # 7. 返回CookieJar物件:
 6 cookiejar = response.cookies
 7 
 8 # 8. 將CookieJar轉為字典:
 9 cookiedict = requests.utils.dict_from_cookiejar(cookiejar)
10 
11 print (cookiejar)
12 
13 print (cookiedict)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

session

實現人人網登入

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

import requests
# 1. 建立session物件,可以儲存Cookie值
ssion = requests.session()
# 2. 處理 headers
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# 3. 需要登入的使用者名稱和密碼
data = {"email":"", "password":""} 
# 4. 傳送附帶使用者名稱和密碼的請求,並獲取登入後的Cookie值,儲存在ssion裡
ssion.post("http://www.renren.com/PLogin.do", data = data)
# 5. ssion包含使用者登入後的Cookie值,可以直接訪問那些登入後才可以訪問的頁面
response = ssion.get("http://www.renren.com/410043129/profile")
# 6. 列印響應內容
print (response.text)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

處理HTTPS請求 SSL證書驗證

Requests也可以為HTTPS請求驗證SSL證書:

  • 要想檢查某個主機的SSL證書,你可以使用 verify 引數(也可以不寫)
  • 如果SSL證書驗證不通過,或者不信任伺服器的安全證書,則會報出SSLError,據說 12306 證書是自己做的
  • 如果我們想跳過 12306 的證書驗證,把 verify 設定為 False 就可以正常請求了
SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

非結構化資料與結構化資料提取

XPath與lxml類庫

  • bookstore//book
選擇屬於 bookstore 元素的後代的所有 book 元素,而不管它們位於 bookstore 之下的什麼位置
  • /bookstore/book[last()-1]
選取屬於 bookstore 子元素的倒數第二個 book 元素
  • /bookstore/book[position()<3]
選取最前面的兩個屬於 bookstore 元素的子元素的 book 元素
  • /bookstore/book[price>35.00]/title
選取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值須大於 35.00
  • //book/title | //book/price
選取 book 元素的所有 title 和 price 元素
  • //title | //price
選取文件中的所有 title 和 price 元素
  • /bookstore/book/title | //price
選取屬於 bookstore 元素的 book 元素的所有 title 元素,以及文件中所有的 price 元素
  • //li//span
獲取<li> 標籤下的所有 <span> 標籤
  • //li/a//@class
獲取 <li> 標籤下的<a>標籤裡的所有 class
  • //li[last()]/a/@href
獲取最後一個 <li> 的 <a> 的 href

JSON

json.loads()

從json到python的型別轉化

json.dumps()

從python原始型別向json型別的轉化

chardet是一個非常優秀的編碼識別模組,可通過pip安裝。chardet.detect()返回字典, 其中confidence是檢測精確度

json.dump()

將Python內建型別序列化為json物件後寫入文

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

1 import json
2 
3 listStr = [{"city": "北京"}, {"name": "大劉"}]
4 json.dump(listStr, open("listStr.json","w"), ensure_ascii=False)
5 
6 dictStr = {"city": "北京", "name": "大劉"}
7 json.dump(dictStr, open("dictStr.json","w"), ensure_ascii=False)

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

json.load()

讀取檔案中json形式的字串元素 轉化成python型別

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 import json
 2 
 3 strList = json.load(open("listStr.json"))
 4 print strList
 5 
 6 # [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}]
 7 
 8 strDict = json.load(open("dictStr.json"))
 9 print strDict
10 # {u'city': u'\u5317\u4eac', u'name': u'\u5927\u5218'}

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

Queue

  1. Queue是python中的標準庫,可以直接import Queue引用;佇列是執行緒間最常用的交換資料的形式
  2. 對於資源,加鎖是個重要的環節。因為python原生的list,dict等,都是not thread safe的。而Queue,是執行緒安全的,因此在滿足使用條件下,建議使用佇列
  3. 初始化: class Queue.Queue(maxsize) FIFO 先進先出
  4. 包中的常用方法:
  • Queue.qsize() 返回佇列的大小
  • Queue.empty() 如果佇列為空,返回True,反之False
  • Queue.full() 如果佇列滿了,返回True,反之False
  • Queue.full 與 maxsize 大小對應
  • Queue.get([block[, timeout]])獲取佇列,timeout等待時間
  1. 建立一個“佇列”物件
 import Queue
 myqueue = Queue.Queue(maxsize = 10)
  1. 將一個值放入佇列中
myqueue.put(10)
  1. 將一個值從佇列中取出
myqueue.get()

BeautifulSoup4

lxml 只會區域性遍歷,而Beautiful Soup 是基於HTML DOM的,會載入整個文件,解析整個DOM樹,因此時間和記憶體開銷都會大很多,所以效能要低於lxml

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 print soup.name
 2 # [document] #soup 物件本身比較特殊,它的 name 即為 [document]
 3 
 4 print soup.head.name
 5 # head #對於其他內部標籤,輸出的值便為標籤本身的名稱
 6 
 7 print soup.p.attrs
 8 # {'class': ['title'], 'name': 'dromouse'}
 9 # 在這裡,我們把 p 標籤的所有屬性列印輸出了出來,得到的型別是一個字典。
10 
11 print soup.p['class'] # soup.p.get('class')
12 # ['title'] #還可以利用get方法,傳入屬性的名稱,二者是等價的
13 
14 soup.p['class'] = "newClass"
15 print soup.p # 可以對這些屬性和內容等等進行修改
16 # <p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
17 
18 del soup.p['class'] # 還可以對這個屬性進行刪除
19 print soup.p
20 # <p name="dromouse"><b>The Dormouse's story</b></p>
21 
22 print soup.p.string
23 # The Dormouse's story
24 
25 print type(soup.p.string)
26 # In [13]: <class 'bs4.element.NavigableString'>
27 
28 # Comment 物件是一個特殊型別的 NavigableString 物件,其輸出的內容不包括註釋符號
29 print soup.a
30 # <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
31 
32 print soup.a.string
33 # Elsie 
34 
35 print type(soup.a.string)
36 # <class 'bs4.element.Comment'>
37 # tag 的 .content 屬性可以將tag的子節點以列表的方式輸出
38 print soup.head.contents 
39 #[<title>The Dormouse's story</title>]
40 print soup.head.contents[0]
41 #<title>The Dormouse's story</title>
42 
43 # .children 返回的是一個 list 生成器物件,通過遍歷獲取所有子節點
44 print soup.head.children
45 #<listiterator object at 0x7f71457f5710>
46 
47 for child in soup.body.children:
48 print child
49 
50 # 所有子孫節點: .descendants 屬性
51 .contents 和 .children 屬性僅包含tag的直接子節點,.descendants 屬性可以對所有tag的子孫節點進行遞迴迴圈,和 children類似,我們也需要遍歷獲取其中的內容
52 for child in soup.descendants:
53 print child

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

動態HTML處理和機器影象識別

Selenium

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 # 呼叫鍵盤按鍵操作時需要引入的Keys包
 2 from selenium.webdriver.common.keys import Keys
 3 
 4 # 生成當前頁面快照並儲存
 5 driver.save_screenshot("baidu.png")
 6 
 7 # 列印網頁渲染後的原始碼
 8 print driver.page_source
 9 
10 # 獲取當前頁面Cookie
11 print driver.get_cookies()
12 
13 # ctrl+a 全選輸入框內容
14 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
15 
16 # ctrl+x 剪下輸入框內容
17 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
18 
19 # 模擬Enter回車鍵
20 driver.find_element_by_id("su").send_keys(Keys.RETURN)
21 
22 # 獲取當前url
23 print driver.current_url
24 
25 # 關閉當前頁面,如果只有一個頁面,會關閉瀏覽器
26 # driver.close()
27 
28 # 關閉瀏覽器
29 driver.quit()

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

頁面操作

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 # 獲取id標籤值
 2 element = driver.find_element_by_id("passwd-id")
 3 # 獲取name標籤值
 4 element = driver.find_element_by_name("user-name")
 5 # 獲取標籤名值
 6 element = driver.find_elements_by_tag_name("input")
 7 # 也可以通過XPath來匹配
 8 element = driver.find_element_by_xpath("//input[@id='passwd-id']")
 9 
10 find_element_by_id
11 find_elements_by_name
12 find_elements_by_xpath
13 find_elements_by_link_text
14 # 上下兩行區別:
15 # partial 是部分的意思,可以定位a標籤文本里的部分內容
16 find_elements_by_partial_link_text
17 find_elements_by_tag_name
18 find_elements_by_class_name
19 find_elements_by_css_selector

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

滑鼠動作鏈

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 #匯入 ActionChains 類
 2 from selenium.webdriver import ActionChains
 3 
 4 # 滑鼠移動到 ac 位置
 5 ac = driver.find_element_by_xpath('element')
 6 ActionChains(driver).move_to_element(ac).perform()
 7 
 8 
 9 # 在 ac 位置單擊
10 ac = driver.find_element_by_xpath("elementA")
11 ActionChains(driver).move_to_element(ac).click(ac).perform()
12 
13 # 在 ac 位置雙擊
14 ac = driver.find_element_by_xpath("elementB")
15 ActionChains(driver).move_to_element(ac).double_click(ac).perform()
16 
17 # 在 ac 位置右擊
18 ac = driver.find_element_by_xpath("elementC")
19 ActionChains(driver).move_to_element(ac).context_click(ac).perform()
20 
21 # 在 ac 位置左鍵單擊hold住
22 ac = driver.find_element_by_xpath('elementF')
23 ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()
24 
25 # 將 ac1 拖拽到 ac2 位置
26 ac1 = driver.find_element_by_xpath('elementD')
27 ac2 = driver.find_element_by_xpath('elementE')
28 ActionChains(driver).drag_and_drop(ac1, ac2).perform()

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

填充表單

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 # 匯入 Select 類
 2 from selenium.webdriver.support.ui import Select
 3 
 4 # 找到 name 的選項卡
 5 select = Select(driver.find_element_by_name('status'))
 6 
 7 # 
 8 select.select_by_index(1)
 9 select.select_by_value("0")
10 select.select_by_visible_text(u"未稽核")

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

以上是三種選擇下拉框的方式,它可以根據索引來選擇,可以根據值來選擇,可以根據文字來選擇。注意:

  • index 索引從 0 開始
  • value是option標籤的一個屬性值,並不是顯示在下拉框中的值
  • visible_text是在option標籤文字的值,是顯示在下拉框的值
  • 全部取消選擇
alert = driver.switch_to_alert()

頁面切換

一個瀏覽器肯定會有很多視窗,所以我們肯定要有方法來實現視窗的切換。切換視窗的方法如下:

driver.switch_to.window("this is window name")

也可以使用 window_handles 方法來獲取每個視窗的操作物件。例如:

for handle in driver.window_handles:
 driver.switch_to_window(handle)

頁面前進和後退

driver.forward() #前進
driver.back() # 後退

Cookies

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

1 for cookie in driver.get_cookies():
2 print "%s -> %s" % (cookie['name'], cookie['value'])
3 
4 # By name
5 driver.delete_cookie("CookieName")
6 
7 # all
8 driver.delete_all_cookies()

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

頁面等待

隱式等待是等待特定的時間,顯式等待是指定某一條件直到這個條件成立時繼續執行。

顯式等待

顯式等待指定某個條件,然後設定最長等待時間。如果在這個時間還沒有找到元素,那麼便會丟擲異常了

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 from selenium import webdriver
 2 from selenium.webdriver.common.by import By
 3 # WebDriverWait 庫,負責迴圈等待
 4 from selenium.webdriver.support.ui import WebDriverWait
 5 # expected_conditions 類,負責條件出發
 6 from selenium.webdriver.support import expected_conditions as EC
 7 
 8 driver = webdriver.Chrome()
 9 driver.get("http://www.xxxxx.com/loading")
10 try:
11 # 頁面一直迴圈,直到 id="myDynamicElement" 出現
12 element = WebDriverWait(driver, 10).until(
13 EC.presence_of_element_located((By.ID, "myDynamicElement"))
14 )
15 finally:
16 driver.quit()

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

如果不寫引數,程式預設會 0.5s 呼叫一次來檢視元素是否已經生成,如果本來元素就是存在的,那麼會立即返回

下面是一些內建的等待條件,你可以直接呼叫這些條件,而不用自己寫某些等待條件了

title_is

title_contains

presence_of_element_located

visibility_of_element_located

visibility_of

presence_of_all_elements_located

text_to_be_present_in_element

text_to_be_present_in_element_value

frame_to_be_available_and_switch_to_it

invisibility_of_element_located

element_to_be_clickable – it is Displayed and Enabled.

staleness_of

element_to_be_selected

element_located_to_be_selected

element_selection_state_to_be

element_located_selection_state_to_be

alert_is_present

隱式等待

不設定,預設等待時間為0,單位為秒

1 from selenium import webdriver
2 
3 driver = webdriver.Chrome()
4 driver.implicitly_wait(10) # seconds
5 driver.get("http://www.xxxxx.com/loading")
6 myDynamicElement = driver.find_element_by_id("myDynamicElement")

def __del__(self):

'''呼叫內建的稀構方法,在程式退出的時候自動呼叫

類似的還可以在檔案開啟的時候呼叫close,資料庫連結的斷開

'''

self.driver.quit()

# 預設這個方法是物件使用後自動銷燬物件用的,在這裡修改為物件使用後關閉瀏覽器 

Tesseract與pytesseract

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 import pytesseract
 2 from PIL import Image
 3 
 4 image = Image.open('test.jpg')
 5 text = pytesseract.image_to_string(image)
 6 print text
 7 對圖片進行閾值過濾和降噪處理
 8 from PIL import Image
 9 import subprocess
10 
11 def cleanFile(filePath, newFilePath):
12 image = Image.open(filePath)
13 
14 # 對圖片進行閾值過濾(低於143的置為黑色,否則為白色)
15 image = image.point(lambda x: 0 if x < 143 else 255)
16 # 重新儲存圖片
17 image.save(newFilePath)
18 
19 # 呼叫系統的tesseract命令對圖片進行OCR識別 
20 subprocess.call(["tesseract", newFilePath, "output"])
21 
22 # 開啟檔案讀取結果
23 with open("output.txt", 'r') as f:
24 print(f.read())
25 
26 if __name__ == "__main__":
27 cleanFile("text2.png", "text2clean.png")

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

scrapy框架

儲存資料

scrapy儲存資訊的最簡單的方法主要有四種,-o 輸出指定格式的檔案,,命令如下:

# json格式,預設為Unicode編碼

scrapy crawl itcast -o teachers.json

# json lines格式,預設為Unicode編碼

scrapy crawl itcast -o teachers.jsonl

# csv 逗號表示式,可用Excel開啟

scrapy crawl itcast -o teachers.csv

# xml格式

scrapy crawl itcast -o teachers.xml

元素選取

  • contains的用法,or的用法,last()的含義
response.xpath('//*[contains(@class,"odd") or contains(@class,"even")]/td[last()]/text()').extract()

0-1000隨意設定,數值越低,元件的優先順序越高

parse()方法的工作機制

  • 因為使用的yield,而不是return。parse函式將會被當做一個生成器使用。scrapy會逐一獲取parse方法中生成的結果,並判斷該結果是一個什麼樣的型別
  • 如果是request則加入爬取佇列,如果是item型別則使用pipeline處理,其他型別則返回錯誤資訊
  • scrapy取到第一部分的request不會立馬就去傳送這個request,只是把這個request放到佇列裡,然後接著從生成器裡獲取
  • 取盡第一部分的request,然後再獲取第二部分的item,取到item了,就會放到對應的pipeline裡處理
  • parse()方法作為回撥函式(callback)賦值給了Request,指定parse()方法來處理這些請求 scrapy.Request(url, callback=self.parse)
  • Request物件經過排程,執行生成 scrapy.http.response()的響應物件,並送回給parse()方法,直到排程器中沒有Request(遞迴的思路)
  • 取盡之後,parse()工作結束,引擎再根據佇列和pipelines中的內容去執行相應的操作
  • 程式在取得各個頁面的items前,會先處理完之前所有的request佇列裡的請求,然後再提取item
  • 這一切的一切,Scrapy引擎和排程器將負責到底

CrawlSpiders

rule

CrawlSpider使用rules來決定爬蟲的爬取規則,並將匹配後的url請求提交給引擎。所以在正常情況下,CrawlSpider不需要單獨手動返回請求了

  • link_extractor:是一個Link Extractor物件,用於定義需要提取的連結
  • callback: 從link_extractor中每獲取到連結時,引數所指定的值作為回撥函式,該回調函式接受一個response作為其第一個引數

注意:當編寫爬蟲規則時,避免使用parse作為回撥函式。由於CrawlSpider使用parse方法來實現其邏輯,如果覆蓋了 parse方法,crawl spider將會執行失敗

  • follow:是一個布林(boolean)值,指定了根據該規則從response提取的連結是否需要跟進。 如果callback為None,follow 預設設定為True ,否則預設為False
  • process_links:指定該spider中哪個的函式將會被呼叫,從link_extractor中獲取到連結列表時將會呼叫該函式。該方法主要用來過濾
  • process_request:指定該spider中哪個的函式將會被呼叫, 該規則提取到每個request時都會呼叫該函式。 (用來過濾request)

Request和Response

傳送POST請求

  • 可以使用 yield scrapy.FormRequest(url, formdata, callback)方法傳送POST請求
  • 如果希望程式執行一開始就傳送POST請求,可以重寫Spider類的start_requests(self) 方法,並且不再呼叫start_urls裡的url

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 class mySpider(scrapy.Spider):
 2 # start_urls = ["http://www.example.com/"]
 3 
 4 def start_requests(self):
 5 url = 'http://www.renren.com/PLogin.do'
 6 
 7 # FormRequest 是Scrapy傳送POST請求的方法
 8 yield scrapy.FormRequest(
 9 url = url,
10 formdata = {"email" : "[email protected]", "password" : "axxxxxxxe"},
11 callback = self.parse_page
12 )
13 def parse_page(self, response):
14 # do something

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

模擬登陸

  • 使用FormRequest.from_response()方法模擬使用者登入
  • 通常網站通過 實現對某些表單欄位(如資料或是登入介面中的認證令牌等)的預填充
  • 使用Scrapy抓取網頁時,如果想要預填充或重寫像使用者名稱、使用者密碼這些表單欄位, 可以使用 FormRequest.from_response() 方法實現

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 import scrapy
 2 
 3 class LoginSpider(scrapy.Spider):
 4 name = 'example.com'
 5 start_urls = ['http://www.example.com/users/login.php']
 6 
 7 def parse(self, response):
 8 return scrapy.FormRequest.from_response(
 9 response,
10 formdata={'username': 'john', 'password': 'secret'},
11 callback=self.after_login
12 )
13 
14 def after_login(self, response):
15 # check login succeed before going on
16 if "authentication failed" in response.body:
17 self.log("Login failed", level=log.ERROR)
18 return

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

中介軟體

防止爬蟲被反

  • 動態設定User-Agent(隨機切換User-Agent,模擬不同使用者的瀏覽器資訊)
  • 禁用Cookies(也就是不啟用cookies middleware,不向Server傳送cookies,有些網站通過cookie的使用發現爬蟲行為)
  • 可以通過COOKIES_ENABLED 控制 CookiesMiddleware 開啟或關閉
  • 設定延遲下載(防止訪問過於頻繁,設定為 2秒 或更高)
  • Google Cache 和 Baidu Cache:如果可能的話,使用谷歌/百度等搜尋引擎伺服器頁面快取獲取頁面資料
  • 使用IP地址池:VPN和代理IP,現在大部分網站都是根據IP來ban的
  • 使用 Crawlera(專用於爬蟲的代理元件),正確配置和設定下載中介軟體後,專案所有的request都是通過crawlera發出

DOWNLOADER_MIDDLEWARES = {

'scrapy_crawlera.CrawleraMiddleware': 600

}

CRAWLERA_ENABLED = True

CRAWLERA_USER = '註冊/購買的UserKey'

CRAWLERA_PASS = '註冊/購買的Password'

下載中介軟體

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

 1 # middlewares.py
 2 
 3 #!/usr/bin/env python
 4 # -*- coding:utf-8 -*-
 5 
 6 import random
 7 import base64
 8 
 9 from settings import USER_AGENTS
10 from settings import PROXIES
11 
12 # 隨機的User-Agent
13 class RandomUserAgent(object):
14 def process_request(self, request, spider):
15 useragent = random.choice(USER_AGENTS)
16 
17 request.headers.setdefault("User-Agent", useragent)
18 
19 class RandomProxy(object):
20 def process_request(self, request, spider):
21 proxy = random.choice(PROXIES)
22 
23 if proxy['user_passwd'] is None:
24 # 沒有代理賬戶驗證的代理使用方式
25 request.meta['proxy'] = "http://" + proxy['ip_port']
26 else:
27 # 對賬戶密碼進行base64編碼轉換
28 base64_userpasswd = base64.b64encode(proxy['user_passwd'])
29 # 對應到代理伺服器的信令格式裡
30 request.headers['Proxy-Authorization'] = 'Basic ' + base64_userpasswd
31 request.meta['proxy'] = "http://" + proxy['ip_port']

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結!

 

為什麼HTTP代理要使用base64編碼: HTTP代理的原理很簡單,就是通過HTTP協議與代理伺服器建立連線,協議信令中包含要連線到的遠端主機的IP和埠號,如果有需要身份驗證的話還需要加上授權資訊,伺服器收到信令後首先進行身份驗證,通過後便與遠端主機建立連線,連線成功之後會返回給客戶端200,表示驗證通過,就這麼簡單,下面是具體的信令格式:

settings

  • BOT_NAME
  • 預設: 'scrapybot'
  • 當您使用 startproject 命令建立專案時其也被自動賦值
  • CONCURRENT_ITEMS
  • 預設: 100
  • Item Processor(即 Item Pipeline) 同時處理(每個response的)item的最大值
  • CONCURRENT_REQUESTS
  • 預設: 16
  • Scrapy downloader 併發請求(concurrent requests)的最大值
  • DEFAULT_REQUEST_HEADERS
  • 預設: 如下

{

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

'Accept-Language': 'en',

}

Scrapy HTTP Request使用的預設header。
  • DEPTH_LIMIT
  • 預設: 0
  • 爬取網站最大允許的深度(depth)值。如果為0,則沒有限制
  • DOWNLOAD_DELAY
  • 預設: 0
  • 下載器在下載同一個網站下一個頁面前需要等待的時間。該選項可以用來限制爬取速度, 減輕伺服器壓力。同時也支援小數
  • DOWNLOAD_DELAY = 0.25 # 250 ms of delay
  • 預設情況下,Scrapy在兩個請求間不等待一個固定的值, 而是使用0.5到1.5之間的一個隨機值 * DOWNLOAD_DELAY 的結果作為等待間隔
  • DOWNLOAD_TIMEOUT
  • 預設: 180
  • 下載器超時時間(單位: 秒)
  • ITEM_PIPELINES
  • 預設: {}
  • 儲存專案中啟用的pipeline及其順序的字典。該字典預設為空,值(value)任意,不過值(value)習慣設定在0-1000範圍內,值越小優先順序越高

ITEM_PIPELINES = {

'mySpider.pipelines.SomethingPipeline': 300,

'mySpider.pipelines.ItcastJsonPipeline': 800,

}

  • LOG_ENABLED
  • 預設: True
  • 是否啟用logging
  • LOG_ENCODING
  • 預設: 'utf-8'
  • logging使用的編碼
  • LOG_LEVEL
  • 預設: 'DEBUG'
  • log的最低級別。可選的級別有: CRITICAL、 ERROR、WARNING、INFO、DEBUG
  • USER_AGENT
  • 預設: "Scrapy/VERSION (+http://scrapy.org)"
  • 爬取的預設User-Agent,除非被覆蓋
  • PROXIES: 代理設定
  • COOKIES_ENABLED = False
  • 禁用Cookies

scrapy-redis

Scrapy-redis提供了下面四種元件(components):(四種元件意味著這四個模組都要做相應的修改)

  • Scheduler
  • Duplication Filter
  • Item Pipeline
  • Base Spider

scrapy-redis的總體思路

  • 這個工程通過重寫scheduler和spider類,實現了排程、spider啟動和redis的互動
  • 實現新的dupefilter和queue類,達到了判重和排程容器和redis的互動,因為每個主機上的爬蟲程序都訪問同一個redis資料庫,所以排程和判重都統一進行統一管理,達到了分散式爬蟲的目的
  • 當spider被初始化時,同時會初始化一個對應的scheduler物件,這個排程器物件通過讀取settings,配置好自己的排程容器queue和判重工具dupefilter
  • 每當一個spider產出一個request的時候,scrapy核心會把這個reuqest遞交給這個spider對應的scheduler物件進行排程,scheduler物件通過訪問redis對request進行判重,如果不重複就把他新增進redis中的排程池
  • 當排程條件滿足時,scheduler物件就從redis的排程池中取出一個request傳送給spider,讓他爬取
  • 當spider爬取的所有暫時可用url之後,scheduler發現這個spider對應的redis的排程池空了,於是觸發訊號spider_idle,spider收到這個訊號之後,直接連線redis讀取strart url池,拿去新的一批url入口,然後再次重複上邊的工作