網絡爬蟲概要

網絡爬蟲是一種按照必定的規則,自動地抓取網站信息的程序或者腳本。css

本文以 Python 爲例簡要談談爬蟲是如何編寫的。html

如何爬取網站信息

寫爬蟲以前,咱們必須確保可以爬取目標網站的信息。python

所以,先要弄清如下三個問題:mysql

  1. 網站是否已經提供了 api
  2. 網站是靜態的仍是動態的
  3. 網站是否有反爬的對策

情形 1:開放 api 的網站

一個網站假若開放了 api,那你就能夠直接 GET 到它的 json 數據。ios

好比xkcd 的 about 頁就提供了 api 供你下載git

import requests
requests.get('https://xkcd.com/614/info.0.json').json()
複製代碼

那麼如何判斷一個網站是否開放 api 呢?有 3 種方法:github

  1. 在站內尋找 api 入口
  2. 用搜索引擎搜索「某網站 api」
  3. 抓包。有的網站雖然用到了 ajax(好比果殼網的瀑布流文章,亦或是unsplash的瀑布流圖片),可是經過抓包仍是可以獲取 XHR 裏的 json 數據的,不要傻乎乎地去用 selenium,反而會下降效率。

怎麼抓包:F12 - Network - F5 刷新web

實際上,app 的數據也能夠經過抓包來獲取。ajax

app 抓包

安裝fiddler並啓動,打開 Tools-Options-Connections,將 Allow remote computers to connect 打上勾並重啓 fiddler。sql

命令行上輸入 ipconfig,查看本身網絡的 ipv4 地址,在手機的網絡上設置 HTTP 代理,端口爲 8888。

這時雖然說能抓到數據,但都是 HTTP 的,而 app 的大部分數據都是 HTTPS 的。

在 Options-HTTPS 中將 Decrypt HTTPS traffic 打上勾。

以 ios 系統爲例,在 Safari 瀏覽器中輸入http://ipv4:8888,下載證書並安裝。

這樣就能抓到 HTTPS 數據了。

情形 2:不開放 api 的網站

若是此網站是靜態頁面,那麼你就能夠解析它的 HTML。

解析庫強烈推薦parsel,不只語法和 css 選擇器相似,並且速度也挺快,Scrapy 用的就是它。

你須要瞭解一下css 選擇器的語法xpath也行),而且學會看網頁的審查元素(F12 鍵)

好比獲取 konachan 的全部原圖連接

from parsel import Selector
res = requests.get('https://konachan.com/post')
tree = Selector(text=res.text)
imgs = tree.css('a.directlink::attr(href)').extract()
複製代碼

若是此網站是動態頁面,先用 selenium 來渲染 JS,再用 HTML 解析庫來解析 driver 的 page_source。

好比獲取 hitomi.la 的數據(這裏把 chrome 設置成了無頭模式)

from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get('https://hitomi.la/type/gamecg-all-1.html')
tree = Selector(text=driver.page_source)
gallery_content = tree.css('.gallery-content > div')
複製代碼

情形 3:反爬的網站

目前的反爬策略常見的有:User-Agent 檢測、Referer 防盜鏈、驗證碼、登陸、封 ip 等。

前兩個最簡單:在請求的 headers 中把它們替換掉就好了

驗證碼:利用打碼平臺破解(若是硬上的話用 opencv 或 keras 訓練圖)

登陸:利用 requests 的 post 或者 selenium 模擬用戶進行模擬登錄

封 ip:買些代理 ip(免費 ip 通常都無論用),requests 中傳入 proxies 參數便可

如何編寫結構化的爬蟲

爬蟲的結構很簡單,無非就是創造出一個 tasklist,對 tasklist 裏的每個 task 調用 crawl 函數。

大多數網頁的 url 構造都是有規律的,你只需根據它用列表推導式來構造出 tasklist

若是追求速度的話,能夠考慮用 concurrent.futures 或者 asyncio 等庫。

from pathlib import Path
from concurrent import futures
import requests
from parsel import Selector

domain = 'https://www.doutula.com'
total = []


def crawl(url):
    res = requests.get(url)
    tree = Selector(text=res.text)
    imgs = tree.css('img.lazy::attr(data-original)').extract()
    total.extend(imgs)


if __name__ == '__main__':
    tasklist = [f'{domain}/article/list/?page={i}' for i in range(1, 551)]
    with futures.ThreadPoolExecutor(50) as executor:
        executor.map(crawl, tasklist)
    Path('doutula.txt').write_text('\n'.join(total))
複製代碼

數據存儲的話,看需求,存到數據庫中的話只需熟悉對應的驅動便可。

經常使用的數據庫驅動有:pymysql(MySQL), pymongo(MongoDB)

還有一種方法就是直接利用現成的框架來編寫:looter是筆者寫的一個輕量級框架,適合中小型項目;比較大型的項目建議用scrapy

相關文章
相關標籤/搜索