天天看點

靜态網頁爬蟲②

文章目錄

  • ​​爬取整個網站​​
  • ​​反爬蟲​​
  • ​​判别身份​​
  • ​​IP 限制​​
  • ​​robots.txt​​

爬取整個網站

為了爬取整個網站,我們得先分析該網站的資料是如何加載的。

還是以某瓣讀書為例,當我們點選第二頁後,觀察浏覽器的位址欄,發現網址變了。網址變成了 https://book.douban.com/top250?start=25,和原來相比後面多了一個 ?start=25。

這部分被稱為 ​

​查詢字元串​

​,查詢字元串作為用于搜尋的參數或處理的資料傳送給伺服器處理,格式是 ?key1=value1&key2=value2。

? 前面是網頁的位址,後面是查詢字元串。以鍵值對 key=value 的形式指派,多個鍵值對之間用 & 連接配接在一起。例如 ?a=1&b=2 表示:a 的值為 1,b 的值為 2。

查詢字元串用于資訊的傳遞,伺服器通過它就能知道你想要什麼,進而給你傳回對應的内容。比如你在知乎搜尋 python,網址會變成 https://www.zhihu.com/search?type=content&q=python,後面的查詢字元串告訴伺服器你想要的是有關 python 的内容,于是伺服器便将有關 python 的内容傳回給你了。

了解了查詢字元串的相關知識後,接下來我們多翻幾頁豆瓣讀書的頁面,觀察一下網址的變化規律:

靜态網頁爬蟲②

第二頁 start=25,第三頁 start=50,第十頁 start=225,而每頁的書籍數量是 25。你有沒有發現其中的規律?

因為每頁展示 25 本書,根據規律其實不難推測出 start 參數表示從第幾本書開始展示,是以第一頁 start 是 0,第二頁 start 是 25,第三頁 start 是 50,第十頁 start 是 225。是以 start 的計算公式為 start = 25 * (頁碼數 - 1)(25 為每頁展示的數量)。我們來通過代碼自動生成某瓣圖書 Top250 所有資料(10 頁)的位址:

url = 'https://book.douban.com/top250?start={}'
# num 從 0 開始是以不用再 -1
urls = [url.format(num * 25) for num in range(10)]
print(urls)
# 輸出:
# [
#   'https://book.douban.com/top250?start=0',
#   'https://book.douban.com/top250?start=25',
#   'https://book.douban.com/top250?start=50',
#   'https://book.douban.com/top250?start=75',
#   'https://book.douban.com/top250?start=100',
#   'https://book.douban.com/top250?start=125',
#   'https://book.douban.com/top250?start=150',
#   'https://book.douban.com/top250?start=175',
#   'https://book.douban.com/top250?start=200',
#   'https://book.douban.com/top250?start=225'
# ]      

有了所有網頁的連結後,我們就可以爬取整個網站的資料了!

我們可以修改代碼,将前文中的某瓣top250進行整個的爬取:

import requests
from bs4 import BeautifulSoup

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32'
}

num = 0

# 為了對整個某瓣進行爬取,我們先對資料處理的部分進行封裝

def getTarget(url):
    global num
    #首先對網頁送出請求并獲得響應
    req = requests.get(url,headers = headers)
    #将網頁的源代碼形式解析
    soup = BeautifulSoup(req.text,'html.parser')
    
    #進行元素的第一次提取
    result1 = soup.select('.item .pic')
    
    for i in result1:
        num += 1
        name = i.select('a img')[0]['alt']
        link = i.select('a')[0]['href']
        print(num,' ',name,link)
        


for i in range(0,230,25):
    address = 'https://movie.douban.com/top250?start={}'.format(i)
    getTarget(address)
    # 暫停 1 秒防止通路太快被封
    time.sleep(1)      

結果(不完整):

靜态網頁爬蟲②

反爬蟲

​反爬蟲​

​ 是網站限制爬蟲的一種政策。它并不是禁止爬蟲(完全禁止爬蟲幾乎不可能,也可能誤傷正常使用者),而是限制爬蟲,讓爬蟲在網站可接受的範圍内爬取資料,不至于導緻網站癱瘓無法運作。

常見的反爬蟲方式有 ​

​判别身份​

​​ 和 ​

​IP 限制​

​ 兩種,我們一一介紹,并給出相應的反反爬蟲技巧。

判别身份

有些網站在識别出爬蟲後,會拒絕爬蟲的通路,比如某瓣。我們以某瓣圖書 Top250 為例,用浏覽器打開是下面這樣的:

靜态網頁爬蟲②

如果用爬蟲直接爬取它,我們看看結果會是什麼:

import requests

res = requests.get('https://book.douban.com/top250')
print(res.text)      

輸出結果為空,什麼都沒有。這是因為某瓣将我們的爬蟲識别了出來并拒絕提供内容。

你可能會有疑問,爬蟲不是模拟浏覽器通路網站、擷取網頁源代碼的嗎?怎麼會被識别出來呢?

其實,不管是浏覽器還是爬蟲,通路網站時都會帶上一些資訊用于身份識别。而這些資訊都被存儲在一個叫 ​

​請求頭(request headers)​

​的地方。

伺服器會通過請求頭裡的資訊來判别通路者的身份。請求頭裡的字段有很多,我們暫時隻需了解 user-agent(使用者代理)即可。user-agent 裡包含了作業系統、浏覽器類型、版本等資訊,通過修改它我們就能成功地僞裝成浏覽器。

我們可以根據下圖的訓示操作,看看請求頭長什麼樣:

靜态網頁爬蟲②

我的浏覽器的 user-agent 是 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36,而 requests 預設的 user-agent 是 python-requests/2.22.0。

預設的 user-agent 和在頭上貼着“我是爬蟲”的紙條沒有什麼差別,難怪會被伺服器識别出來。是以我們需要修改請求頭裡的 user-agent 字段内容,将爬蟲僞裝成浏覽器。是以接下來我們來看看如何在 requests 庫中修改請求頭。

我們打開 requests 的官方文檔(http://cn.python-requests.org/zh_CN/latest/)并搜尋 定制請求頭,找到對應的文檔:

靜态網頁爬蟲②

在 requests 的官方文檔中我們可以看到,我們隻需定義一個 字典(請求頭字段作為鍵,字段内容作為值)傳遞給 headers 參數即可。

除了 user-agent 之外的其他請求頭字段也能以同樣的方式添加進去,但大部分情況下我們隻需要添加 user-agent 字段即可(有些甚至不需要)。當我們加了 user-agent 字段還是無法擷取到資料時,說明該網站還通過别的資訊來驗證身份,我們可以将請求頭裡的字段都添加進去試試。

判别身份是最簡單的一種反爬蟲方式,我們也能通過一行代碼,将爬蟲僞裝成浏覽器輕易地繞過這個限制。是以,大部分網站還會進行 IP 限制 防止過于頻繁的通路。

IP 限制

​IP(Internet Protocol)​

​全稱網際網路協定位址,意思是配置設定給使用者上網使用的網際協定的裝置的數字标簽。你可以将 IP 位址了解為門牌号,我隻要知道你家的門牌号就能找到你家。

IP 位址也是一樣,隻要知道伺服器的 IP 位址就能找到對應的伺服器。你在網上沖浪時電腦也會被配置設定一個 IP 位址,這樣伺服器就知道是誰在通路他們的網站了。

前面說過,當我們爬取大量資料時,如果我們不加以節制地通路目标網站,會使網站超負荷運轉,一些個人小網站沒什麼反爬蟲措施可能是以癱瘓。而大網站一般會限制你的通路頻率,因為正常人是不會在 1s 内通路幾十次甚至上百次網站的。

是以,如果你通路過于頻繁,即使改了 user-agent 僞裝成浏覽器了,也還是會被識别為爬蟲,并限制你的 IP 通路該網站。

是以,我們常常使用 time.sleep() 來降低通路的頻率,比如前文中的爬取整個網站的代碼,我們每爬取一個網頁就暫停一秒。這樣,對網站伺服器的壓力不會太大,對方也就睜一隻眼閉一隻眼不理會我們的爬蟲。雖然速度較慢,但也能擷取到我們想要的資料了。

除了降低通路頻率之外,我們也可以使用代理來解決 IP 限制的問題。代理的意思是通過别的 IP 通路網站。這樣,在 IP 被封後我們可以換一個 IP 繼續爬取資料,或者每次爬取資料時都換不同的 IP,避免同一個 IP 通路的頻率過高,這樣就能快速地大規模爬取資料了。

我們打開官方文檔,看看如何使用代理:

靜态網頁爬蟲②

和 headers 一樣,也是定義一個字典,但傳遞給的是 proxies 參數。我們需要将 http 和 https 這兩種協定作為鍵,對應的 IP 代理作為值,最後将整個字典作為 proxies 參數傳遞給 requests.get() 方法即可。IP 代理有免費的和收費的,你可以自行在網上尋找。

提示:http 和 https 都是浏覽器和伺服器之間通訊的協定,https 更加的安全,也被越來越多的網站所使用。

官方文檔中給了代理的基本用法,但在爬取大量資料時我們需要很多的 IP 用于切換。是以,我們需要建立一個 IP 代理池(清單),每次從中随機選擇一個傳給 proxies 參數。我們來看一下如何實作:

import requests
import random
from bs4 import BeautifulSoup

def get_douban_books(url, proxies):
  headers = {
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
  }
  # 使用代理爬取資料
  res = requests.get(url, proxies=proxies, headers=headers)
  soup = BeautifulSoup(res.text, 'html.parser')
  items = soup.find_all('div', class_='pl2')
  for i in items:
    tag = i.find('a')
    name = tag['title']
    link = tag['href']
    print(name, link)

url = 'https://book.douban.com/top250?start={}'
urls = [url.format(num * 25) for num in range(10)]
# IP 代理池(瞎寫的并沒有用)
proxies_list = [
  {
    "http": "http://10.10.1.10:3128",
    "https": "http://10.10.1.10:1080",
  },
  {
    "http": "http://10.10.1.11:3128",
    "https": "http://10.10.1.11:1080",
  },
  {
    "http": "http://10.10.1.12:3128",
    "https": "http://10.10.1.12:1080",
  }
]
for i in urls:
  # 從 IP 代理池中随機選擇一個
  proxies = random.choice(proxies_list)
  get_douban_books(i, proxies)      

這樣,我們就能既快速又不會被限制地爬取資料了。但毫無節制的爬蟲已經和網絡攻擊沒什麼差別了,嚴重的甚至會導緻網站癱瘓。惡意地大量通路别人的網站,消耗伺服器資源是非常不道德、甚至違法的。

其實,除了反爬蟲措施遠不隻這兩種。比如驗證碼,當你的操作異常時網站經常會彈出驗證碼。驗證碼技術也在不斷地更新,從原先簡單的随機生成的數字字母,演變成随機漢字,再由按順序點選圖中對應的漢字演變成滑塊拼圖。

還有些驗證碼是一個數學題,你得計算出答案。像 12306 的驗證碼就更複雜了,需要識别出圖中的物體。驗證碼的形式千千萬萬,破解起來也較為困難。我們這裡不涉及。

了解了一些反爬蟲措施和反反爬蟲技巧後,我們來看看爬蟲中的君子協定—robots.txt。

robots.txt

robots.txt 是一種存放于網站根目錄下的文本檔案,用于告訴爬蟲此網站中的哪些内容是不應被爬取的,哪些是可以被爬取的。

我們隻要在網站域名後加上 /robots.txt 即可檢視,比如某瓣讀書的 robots.txt 位址是:https://book.douban.com/robots.txt。打開它後的内容如下:

User-agent: *
Disallow: /subject_search
Disallow: /search
Disallow: /new_subject
Disallow: /service/iframe
Disallow: /j/

User-agent: Wandoujia Spider
Disallow: /      

User-agent: * 表示針對所有爬蟲(* 是通配符),接下來是符合該 user-agent 的爬蟲要遵守的規則。比如 Disallow: /search 表示禁止爬取 /search 這個頁面,其他同理。