(python)爬蟲----八個專案帶你進入爬蟲的世界

憨了吧唧的人於八發表於2021-07-17

目錄

(一)入門--豆瓣

一、學前要求知識

二、目標

三、什麼是爬蟲

四、爬取網頁

五、解析網頁

六、翻頁

八、程式碼

(二)你的心,我懂得~~--彼岸圖網

一、目標

二、爬取網頁

三、儲存圖片

四、程式碼

(三)視訊--虎牙小姐姐跳舞視訊

一、目標

 二、獲取詳情頁連結

三、獲取視訊地址

四、儲存資料

五、完整程式碼

 (四)登入--CSDN

一、目標

 二、如何登入

三、傳參連結

四、程式碼構建

五、完整程式碼

(五)、模擬人操作瀏覽器(selenium)

一、引子

二、selenium下載

三、selenium+python

四、程式碼

五、後記

(六)爬蟲框架--scrapy的使用(汽車之家)

一、為什麼要使用scrapy

二、scrapy框架整體架構

三、scrapy下載

四、建立scrapy專案

五、建立爬蟲檔案

六、爬取汽車之家

(七)爬蟲注意事項

一、Robots協議

二、遵守Robots協議也有可能犯法

(八)反爬與反反爬

        反爬手段與處理措施

                headers欄位限制和POST、GET請求引數限制

                IP限制

                JS加密

                等

後續


(一)入門--豆瓣

一、學前要求知識

  1.  python基礎
  2.  前端基礎

二、目標

     要爬的網站是豆瓣電影 Top 250 (douban.com)

     需要爬到每個電影的排名、名字、以及評分。

       這是不少人踏上爬蟲之路的第一個網站,現在讓我們來邁出爬蟲的第一步吧!

三、什麼是爬蟲

        簡單來說:爬蟲就是用機器來模擬人瀏覽網頁的過程。

四、爬取網頁

        使用python來爬蟲,需要藉助一個庫,這個庫不是python自帶的,所以需要下載,下載方式就跟正常下載庫的方法一樣。

        匯入該庫,就不在話下了吧!

        在正式爬取網頁前,我們需要了解,平時我們瀏覽網頁的過程實際上就是向瀏覽器發起請求的過程。而請求又分很多種,最常見的為GET和POST請求,在這裡我們暫時不解釋GET和POST的區別。

        我們要爬取豆瓣250時,當然也要知道豆瓣250的請求方式,我們用瀏覽器進入豆瓣250,然後按F12或者點選滑鼠右鍵進入開發者模式,然後點選上方的網路,或者是英文 Network。

然後重新整理頁面,發現突然出現了一大堆,不要慌,這個就是我們在傳送請求時,伺服器和客戶端進行網路傳輸傳送與接收的資料包,隨便點選一個,出現一個框框,然後點選框框上方的預覽或者英文Preview,會大致明白這個資料包傳輸的是什麼資料。

        最終找到了傳輸的資料對我們有用的資料包,再點回標頭或者英文Headers,會在常規內看見請求方式的字樣,後面跟著的就是該資料包的請求方式,豆瓣250的請求方式則是GET。等等先不要關閉開發者模式,我們再往下看還有一個請求標頭,那這是什麼啊?這個對於爬蟲來講是一層障礙,因為有些網站它會檢查請求頭,以此來判斷是爬蟲還是正常訪問。在爬取網頁時,一般都要加一個user-agent,以防網頁有輕微的反爬操作,就導致該爬蟲不能使用了。接著再點選響應或者英文Response,這就是伺服器傳送資料,即響應正文。那麼進行爬蟲的前期準備就完成了。(通常你對哪個連結傳送的請求,請求資訊就在哪個資料包內)

import requests

url = f'https://movie.douban.com/top250'
headers = {
        'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like                     Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
    }
html = requests.get(url, headers=headers)
web_page = html.text

        因為是GET請求,所以要用requests的get方法。列印html,會發現它是Response物件,它有一個text方法,會獲取該請求的資料資訊,我們列印web_page,發現列印的結果是html原始碼也就是該資料包的響應正文。那麼我們也完成了一次爬取。但是爬取出來的資料好像用處不大,這樣的資料還不如正常訪問網站的用處大,那麼爬蟲的用處也完全沒有嘛。但是,爬蟲還有一個重要的環節,那就是清洗資料,一個網頁上的資料肯定是有一點對我們有用,大部分對我們用處不大,所以需要將有用的資料清洗出來。

五、解析網頁

       下面要清洗網頁上的資料,這一過程叫網頁解析。

        解析網頁一般有兩種方法:

  1. 正則匹配(也就是python中re庫)正規表示式,re,Python,正規表示式例項
  2. Xpath。

        今天我使用xpath來解析網頁。

        在python中使用xpath,需要下載lxml解析庫。

pip install lxml

        使用前需要先將html原始碼例項化成etree物件。

from lxml import etree

movie = etree.HTML(web_page)

        接著使用etree物件的xpath屬性,然後寫入要匹配的路徑即可。(成塊爬取,將一個電影的所有資訊先爬下來,然後在爬需要的資訊,這樣爬取的資訊才一定會匹配。)

movies = middle.xpath('//*[@class="item"]')

    for movie in movies:
        rank = movie.xpath('.//em/text()')[0]
        title = movie.xpath('.//span[1]/text()')[0]
        score = movie.xpath('.//*[@class="rating_num"]/text()')[0]
        print(f'排名:{rank},電影名:{title},評分:{score}')

        這樣就完成資料的清洗。

六、翻頁

        聰明的你肯定發現我們只爬到了一頁資料。

        那麼怎麼爬取豆瓣250的所有資料呢,我們先不著急寫程式碼,先翻在瀏覽器上翻上幾頁觀察url(網址)的變化,發現url後面的start,每次都加25。你是不是想到了怎麼爬取所有的資料,等等先不要著急,再試下第一頁是否符合該規律,因為有些網址的第一頁會與後面的沒有規律,當然豆瓣250是符合規律的。

八、程式碼

import requests
from lxml import etree

total_page = 10

for page in range(0, total_page * 25, 25):
    url = f'https://movie.douban.com/top250?start={page}&filter='
    headers = {
        'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
    }

    html = requests.get(url, headers=headers)
    web_page = html.text
    middle = etree.HTML(web_page)
    movies = middle.xpath('//*[@class="item"]')

    for movie in movies:
        rank = movie.xpath('.//em/text()')[0]
        title = movie.xpath('.//span[1]/text()')[0]
        score = movie.xpath('.//*[@class="rating_num"]/text()')[0]
        print(f'排名:{rank},電影名:{title},評分:{score}')

(二)你的心,我懂得~~--彼岸圖網

一、目標

        要爬的網站是4K美女桌布_高清4K美女圖片_彼岸圖網 (netbian.com)

        需要爬到每個美女圖片,並將其儲存下來。

二、爬取網頁

        可以快速得知這個網頁是get請求,於是有了以下程式碼:

import requests

url = 'https://pic.netbian.com/4kmeinv/'
headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
}
html = requests.get(url, headers=headers)
web_page = html.text

        列印web_page,發現列印內容是亂碼。在以下的這個位置一般都會有這個網站的編碼格式

web_page = html.text.encode('iso-8859-1').decode('gbk')

        (前面的編碼形式encode是哪個編碼格式是我試出來的,一般都是utf-8。至於為什麼這個是iso-8859-1,從網上搜尋的答案是ISO-8859-1 是大多數瀏覽器預設的字符集

        這樣輸出的就是正常的html原始碼啦

        由於在列表頁的圖片不清晰,所以需要進入詳情頁下載圖片,發現詳情頁連結都在li下,但是抓取到的li中的連結只有一半,需要加上所缺失的。再瀏覽器中進入詳情頁分析缺失那一部分,多分析幾個,就會發現它們缺失的是什麼啦。

        仔細觀察網頁會發現有一個特殊的圖片,它不是我們想要的小姐姐,但是列印出html原始碼會發現它並沒有出現在原始碼中。但是如果它出現在原始碼中,可以利用它多個class屬性進行排除。

lis = web_html.xpath('//*[@class="slist"]//li')

base_url = 'https://pic.netbian.com'
for li in lis:
    original_url = base_url + li.xpath('.//a/@href')[0]
    li_class = li.xpath('./@class')

         有了詳情頁的url後,自然是再傳送一次請求進入詳情頁,取到圖片啦~~(記得圖片名也要取到哦,取圖片名字是為了作為儲存得檔名。同樣這裡的圖片連結也不全,需要自己補充)

original_html = requests.get(original_url, headers=headers)
        original_page = original_html.text.encode('iso-8859-1').decode('gbk')
        original_page = etree.HTML(original_page)
        img_name = original_page.xpath('//*[@id="img"]/img/@title')[0]
        img_path = 'https://pic.netbian.com/'\
                   + original_page.xpath('//*[@id="img"]/img/@src')[0]

        (點選下載原圖需要登入,所以這裡是直接爬取的圖片)

三、儲存圖片

        首先在與爬蟲檔案同一級處建立一個資料夾用來儲存小姐姐。

        下載文件(圖片是文件的一種):其實也就是向檔案中寫入資料

        讀寫檔案是爬蟲經常使用的操作。

知識補充:IO操作:讀寫檔案是最常見的IO操作。IO操作還包括訪問網路,等待使用者輸入資料。IO操作相對於記憶體計算來說是比較慢的。

        python的讀寫操作,相信大家瞭解(不瞭解的可以先補充下)

        在這裡呢,不推薦使用

f = open('abc1.txt', 'w')
f.write('123')
# 此處可能有很多其他寫入操作
f.write('456')
f.close()

        最好使用(原因,有興趣的可以搜尋瞭解一下)

    with open('./儲存路徑', 'wb') as f:  # wb是以二進位制寫入內容
        f.write('123')

        儲存圖片等資料時,都是儲存的二進位制,讀時電腦會根據字尾來處理。因為這個網站圖片的字尾都是jpg,所以我們就直接寫上。(學過正則得可以使用正則匹配字尾,以防有特殊的)

img_html = requests.get(url=img_path, headers=headers)
img = img_html.content  # 這裡要用content,不能用text,因為它是二進位制的

with open(f'./美女圖片/{img_name}.jpg', 'wb') as f:
    f.write(img)

        這樣就完成了一頁的儲存。要爬取所有頁的圖片,同樣對每頁的url進行分析。發現除第一頁外,剩餘的頁數連結都是:https://pic.netbian.com/4kmeinv/index_{頁數}.html。只需要將第一頁特殊處理一下。

all_page = 145  # 總頁數
for page in range(1, all_page + 1):
    if page == 1:
        url = 'https://pic.netbian.com/4kmeinv/index.html'
    else:
        url = f'https://pic.netbian.com/4kmeinv/index_{page}.html'

四、程式碼

        最後將程式碼模組化,整理一下。

from lxml import etree
import requests
import time

headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
}


# 得到詳細連結
def get_original_url(url):
    html = requests.get(url, headers=headers)
    web_page = html.text.encode('iso-8859-1').decode('gbk')
    web_html = etree.HTML(web_page)
    lis = web_html.xpath('//*[@class="slist"]//li')
    return lis


# 得到圖片
def get_img(original_url):
    original_html = requests.get(original_url, headers=headers)
    original_page = original_html.text.encode('iso-8859-1').decode('gbk')
    original_page = etree.HTML(original_page)
    img_name = original_page.xpath('//*[@id="img"]/img/@title')[0]
    img_path = 'https://pic.netbian.com/' \
               + original_page.xpath('//*[@id="img"]/img/@src')[0]

    img_html = requests.get(url=img_path, headers=headers)
    img = img_html.content
    return img, img_name


# 下載圖片
def download_img(img, img_name):
    with open(f'./美女圖片/{img_name}.jpg', 'wb') as f:
        f.write(img)


if __name__ == '__main__':  # 這樣可以顯得自己是一個高手,嗯~
    all_page = 3  # 總頁數
    for page in range(1, all_page + 1):
        # time.sleep(1)  如果爬取中,發現需要輸入驗證碼、不能爬取,加上這個。
        if page == 1:
            url = 'https://pic.netbian.com/4kmeinv/index.html'
        else:
            url = f'https://pic.netbian.com/4kmeinv/index_{page}.html'

        lis = get_original_url(url)
        for li in lis:
            original_url = 'https://pic.netbian.com' + li.xpath('.//a/@href')[0]
            img, img_name = get_img(original_url)
            download_img(img, img_name)

(三)視訊--虎牙小姐姐跳舞視訊

一、目標

        目標網站:跳舞視訊_跳舞教學|比賽|搞笑|直播視訊中心_虎牙視訊 (huya.com)

        目標:爬取每個小姐姐跳舞的視訊,並將其儲存。 

 二、獲取詳情頁連結

        對於視訊的獲取,同圖片獲取相同。我們需要進入詳情頁獲取視訊的地址。

        通過分析網頁的原始碼,可以發現,每個視訊的詳情頁地址都在li標籤下。

         通過requests庫來獲取網頁的原始碼,並通過解析網頁的原始碼來獲取得到當前頁面中所有詳情頁的連結地址。

all_page = 1  # 500
for page in range(1, all_page+1):
    url = f'https://v.huya.com/g/Dance?set_id=31&order=hot&page={page}'
    html = requests.get(url, headers=headers)
    web_page = html.text
    web_html = etree.HTML(web_page)

    lis = web_html.xpath('//*[@class="vhy-video-list w215 clearfix"]//li')
    for li in lis:
        meta = {
            'name': li.xpath('./a/@title')[0],  # 也可以在詳情頁爬取
            'url': 'https://v.huya.com/' + li.xpath('./a/@href')[0],
        }

        通過for迴圈,來構建所有的列表頁連結,並爬取url。

三、獲取視訊地址

        獲取視訊地址相較於詳情頁連結就比較難啦。

        對原始碼進行分析,可以知道視訊地址在一個video標籤下。

        然後我們寫出分析的xpath路徑,卻發現返回的是空列表。原來他是動態載入的,需要找到對應的請求介面連結。開啟開發者模式,複製視訊地址,在網路選項下通過快捷鍵ctrl+F來搜尋”//huya-w20.huya.com/“,就可以找到對應的請求介面連結。

         對視訊的請求連結進行分析,去除一些不重要的引數,可以得到如下的連結‘https://liveapi.huya.com/moment/getMomentContent?&videoId=468682371’,每個視訊videold的引數與詳情頁地址中的(如下)一一對應。

        視訊的請求連結所返回的是json資料內容,通過對json的分析就可以找到視訊地址。                                        JSON線上解析及格式化驗證 - JSON.cn

        接下來我們編寫程式來爬取視訊地址。

get_video_url = 'https://liveapi.huya.com/moment/getMomentContent?videoId=' + meta['url'].replace('/play/',
                                                                                                          '').replace(
            '.html', '')
        video_web_json = requests.get(get_video_url, headers=headers).json()
        # 原畫
        video_url = video_web_json['data']['moment']['videoInfo']['definitions'][0]['url']

四、儲存資料

        視訊的儲存方法與圖片的一樣,就不多bb了,直接放程式碼

video = requests.get(video_url, headers=headers).content

        with open(f'./美女跳舞視訊/{meta["name"]}.mp4', 'wb') as f:
            f.write(video)

五、完整程式碼

import requests
from lxml import etree

headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
}


# 獲取視訊名和引數
def get_original():
    apage_video = []
    all_page = 1  # 500
    for page in range(1, all_page + 1):
        url = f'https://v.huya.com/g/Dance?set_id=31&order=hot&page={page}'
        html = requests.get(url, headers=headers)
        web_page = html.text
        web_html = etree.HTML(web_page)

        lis = web_html.xpath('//*[@class="vhy-video-list w215 clearfix"]//li')
        for li in lis:
            meta = {
                'name': li.xpath('./a/@title')[0],  # 也可以在詳情頁爬取
                'url': li.xpath('./a/@href')[0],
            }
            apage_video.append(meta)
        return apage_video


# 拼接視訊地址並儲存視訊
def pdd_video_url(meta):
    get_video_url = 'https://liveapi.huya.com/moment/getMomentContent?videoId=' + meta['url'].replace('/play/',
                                                                                                      '').replace(
        '.html', '')
    video_web_json = requests.get(get_video_url, headers=headers).json()
    # 原畫
    video_url = video_web_json['data']['moment']['videoInfo']['definitions'][0]['url']
    video = requests.get(video_url, headers=headers).content

    with open(f'./美女跳舞視訊/{meta["name"]}.mp4', 'wb') as f:
        f.write(video)


if __name__ == '__main__':
    for meta in get_original():
        pdd_video_url(meta)

 (四)登入--CSDN

一、目標

        目標網站:CSDN-專業IT技術社群-登入

        目標:登入CSDN

 二、如何登入

        眾所周知登入是需要賬號和密碼,然後伺服器驗證,驗證通過後才是登入成功。那麼我們是怎麼樣把賬號密碼傳遞給伺服器的呢?

GET

        GET傳遞引數是在請求連結後新增引數傳遞給伺服器的。

        格式:請求連結?&key=value&key1=value1...

POST

        POST傳遞引數是建立一個表單,然後直接將表單傳遞給伺服器。

GET引數在連結後,非常不安全,所以賬號密碼通常是通過POST請求傳遞到。

三、傳參連結

        可以先隨便輸入賬號密碼,然後開啟開發者模式,選中網路,查詢下賬號密碼在哪個請求下。

        經分析發現,是在 'https://passport.csdn.net/v1/register/pc/login/doLogin' 連結的請求下。也就是說將賬號密碼傳送給這個請求,就可以完成登入。

四、程式碼構建

import requests

url = 'https://passport.csdn.net/v1/register/pc/login/doLogin'
headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
}

# 要傳遞的表單
data = {
}

# 因為是POST請求
login = requests.post(url, headers=headers, data=data)

print(login.text)

        那麼表單內填寫什麼內容呢?我們可以看請求中將引數怎麼處理的。

        仿照請求,將引數寫入表單。

data = {
    'userIdentification': '賬號',
    'pwdOrVerifyCode': '密碼'
}

         發起請求,會發現並沒有登入,因為網站檢查了請求頭,請求頭缺少一些內容。

headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
    'Cookie': '開啟開發者模式,點選網路,在請求中檢視自己的Cookie',
    'Content-Type': 'text/plain'
}

        注意:cookie有時間限制,一定時間後需要滑塊,證明自己不是爬蟲。如果發現這種情況,到瀏覽器上手動驗證即可。還是不行的話,換一個cookie。

        再次發起請求

         依然沒有登陸成功,原來這個連結接收的是一個字串,而且還缺失一個必要引數。

data = str({
    'loginType': '1',
    'userIdentification': '賬號',
    'pwdOrVerifyCode': '密碼'
})

        返回這樣的請求即成功登入。

        那麼上面需要的引數怎麼找出來的,可以看 headers請求頭問題 。

五、完整程式碼

import requests

url = 'https://passport.csdn.net/v1/register/pc/login/doLogin'

headers = {
    'User-Agent': 'Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64',
    'Cookie': '開啟開發者模式,點選網路,在請求中檢視自己的Cookie',
    'Content-Type': 'text/plain'
}

# 傳遞的表單
data = str({
    'loginType': '1',
    'userIdentification': '賬號',
    'pwdOrVerifyCode': '密碼'
})

login = requests.post(url, headers=headers, data=data)

# 用於檢視是否登入成功
print(login.text)

(五)、模擬人操作瀏覽器(selenium)

一、引子

        上個專案中滑塊爬蟲應該怎麼處理呢?python程式碼是無法直接操作瀏覽器的,所以需要藉助於其他工具來操作瀏覽器。selenium就是這樣一個工具,它是一個瀏覽器驅動,不僅支援python、還支援其他程式語言。

二、selenium下載

        請根據各自的瀏覽器下載selenium 驅動

  • 谷歌瀏覽器:https://npm.taobao.org/mirrors/chromedriver/
  • 微軟Edge: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
  • 火狐:https://github.com/mozilla/geckodriver/releases
  • Safari:https://webkit.org/blog/6900/webdriver-support-in-safari-10/

        下載時要注意自己的瀏覽器版本,下載和瀏覽器版本最近的那個。

        下載到哪個目錄下都可以,但是最後都要把路徑新增到環境變數Path中。不知道怎麼設定path請自行搜尋。

如果你用其他瀏覽器,或者以上鍊接失效了,請自行搜尋“selenium 驅動”查詢下載地址。

三、selenium+python

        在python中使用selenium需要安裝selenium模組。

        安裝好後可嘗試以下程式碼:正常情況會開啟瀏覽器,顯示百度頁面

from selenium import webdriver
# driver = webdriver.對應的瀏覽器() 如Edge
driver = webdriver.Edge()
driver.get("https://www.baidu.com/")

如出錯請檢查以下幾個點:

  1. selenium 驅動是否設定好了Path
  2. selenium 驅動和你的瀏覽器是否匹配
  3. 是否成功安裝selenium模組

        由於本人瀏覽器更新了,而selenium不經常使用導致selenium 驅動和瀏覽器不匹配,在這裡也不編寫專案啦(其實就是懶~~)

四、程式碼

        這裡放上以前寫的程式碼,對程式碼稍作修改(改上自己用的瀏覽器),可體驗下。

from selenium import webdriver
from lxml import etree
import time


# 準備工作
def open_Edge(url):
    Edge = webdriver.Edge()
    # 最大化瀏覽器
    Edge.maximize_window()
    # 開啟瀏覽器
    Edge.get(f'{url}')
    for i in range(10):
        Edge.execute_script('window.scrollTo(0,document.body.scrollHeight)')
        time.sleep(0.5)

    return Edge


# 獲取網頁原始碼
def get_html(Edge):
    time.sleep(1)

    # 獲取網頁原始碼,與瀏覽網頁時點選檢視原始碼所看到的一樣
    html = Edge.page_source

    return html


def get_book(html):
    tree = etree.HTML(html)

    book_as = tree.xpath("//div[@id='bookItem']/a")

    for book_a in book_as:
        # 獲取書名
        book_name = book_a.xpath("./div[2]/text()")
        # 獲取書的價格
        book_price = book_a.xpath("./div[3]/div[1]/text()")

        print("書名:{name},價格:{price}".format(name=book_name, price=book_price))

    return 0


# 給網頁翻頁
def next_page(Edge):
    # 找到下一頁的按鈕
    next_page = Edge.find_element_by_xpath("//*[@id='entry']/div[2]/div[2]/div[2]/div/button[2]")
    # 點選按鈕
    time.sleep(1)
    next_page.click()

    return '翻頁完成'


def shut_Edge(Edge):
    # 關閉瀏覽器
    # 優雅的關閉
    Edge.quit()


# 主流程
def run_project():
    url = input('請輸入一個url:')
    # URL:https://www.epubit.com/books
    Edge = open_Edge(url)
    html = get_html(Edge)
    get_book(html)
    for i in range(0,10):
        next_page(Edge)
        html = get_html(Edge)
        get_book(html)
    shut_Edge(Edge)


if __name__ == '__main__':
    run_project()

五、後記

        用Selenium驅動真實的瀏覽器抓取資料,缺點是慢,佔用資源多。畢竟它要模擬人操作瀏覽器,開啟瀏覽器。為了解決這個問題,有一種瀏覽器叫無頭瀏覽器(headless),沒有介面,但是可以實現瀏覽器的功能。其中常用的一個無頭瀏覽器叫做phantomJS,可自行了解。

        selenium的功能非常強大,如果需要使用相應的功能,可自行百度。(如:怎麼實現滑塊驗證selenium滑塊驗證 - 國內版 Bing

(六)爬蟲框架--scrapy的使用(汽車之家)

一、為什麼要使用scrapy

  使用request + selenium爬蟲已經可以完成幾乎所有爬蟲專案了,那為什麼還要學習scrapy呢?

        主要為了方便多人開發時,使用的。

在實際開發中(抓取的網站可能成千上萬,甚至更多。有些網站需要重複抓取更新資料)

  • 有的人協調抓取的順序,分配任務和資源,做併發處理;
  • 有的人具體執行下載任務;
  • 有的人負責處理和儲存資料;
  • 有的人負責處理代理IP,過濾資料;(代理IP主要是反爬的一種手段)
  • 等;

scrapy幫我們設計好了程式的框架,做好了基礎工作,比如具體下載我們不用管,多執行緒併發我們也不用管,我們只需要寫各個元件裡的具體業務程式碼就行了。這樣也更容易管理專案。

二、scrapy框架整體架構

兩幅圖一樣

 

三、scrapy下載

pip install scrapy

四、建立scrapy專案

        在使用scrapy爬取前,需要先建立一個scrapy專案。

scrapy startproject 專案名

        專案下面生成了哪些檔案呢?

專案名/

	scrapy.cfg		     # deloy configuration file
	
	ershouche/		     # project's Pyton module, you'll import your code from here
		  __init__.py
		
		  item.py		     # project items definition file
		
		  middleware.py	# project middlewares file
		
		  pipelines.py	 # project pipelines file

		  settings.py	  # project setting file

		  spider/		     # a directory where you'll later put your spider
		     
		       fengtian.py	  # spider file we just create it
			
		       __init__.py

檔案主要有:

  • items.py:用來存放爬蟲獲取的資料模型,也就是事先對資料結構進行定義;

  • middlewares.py:用來存放 middlewares 的檔案,包括 Downloader middlewares 和 Spider middlewares;

  • pipelines.py:用來將 items.py 中的資料模型進行儲存;

  • settings.py:爬蟲的配置資訊(delay,pipeline,headers等);

  • scrapy.cfg:專案配置檔案;

  • spider 資料夾:爬蟲資料夾。

        這些檔案現在不懂也沒關係,後面慢慢學,最終你會理解它們各自的職責。

五、建立爬蟲檔案

       在 spider 下可以手動建立自己的爬蟲檔案,但是 Scrapy 中也提供了相應的命令。

scrapy genspider 爬蟲名 爬取網站

        爬取網站可以在專案中修改。

        進入爬蟲檔案後,裡面程式碼的功能:

# 匯入scrapy模組
import scrapy

# 建立一個爬蟲類,它繼承了scrapy.Spider
class CarsSpider(scrapy.Spider):

    # 爬蟲的名稱,就是我們前面定義的
    name = 'cars'
    
    # 爬蟲允許進入的域,限定爬蟲爬的範圍
    allowed_domains = ['www.che168.com']
    
    # 啟動爬蟲的初始連結,就是爬蟲的起點url
    start_urls = ['http://www.che168.com/']
    
    # 拿到起始url請求的響應(response)後,會傳入parse函式(這個函式名稱不要去改)
    def parse(self, response):
        # 在此處寫爬取、解析網站的程式碼
        pass

六、爬取汽車之家

        先放程式碼

import scrapy
import re


class FengtianSpider(scrapy.Spider):
    name = 'fengtian'
    allowed_domains = ['www.che168.com']
    start_urls = ['https://www.che168.com/china/fengtian/#pvareaid=108402#listfilterstart']

    def parse(self, response):
        cars = response.xpath("//div[@id='goodStartSolrQuotePriceCore0']/ul/li[@name='lazyloadcpc']")
        for car in cars:
            # 取第一個extract_first()
            car_name = car.xpath(".//a/div[@class='cards-bottom']/h4/text()").extract_first()
            price = car.xpath(".//a/div[@class='cards-bottom']/div/span/em/text()").extract_first()
            car_price = price + '萬'

            car_inf = car.xpath(".//a/div[@class='cards-bottom']/p/text()").extract_first()
            # 正則
            car_journey = ''.join(re.findall('.*萬公里', car_inf))
            car_buytime = ''.join(re.findall('\d{4}-\d{2}', car_inf))
            car_city = ''.join(re.findall('.*萬公里/.*/(.*)/', car_inf))

            # 詳情頁
            detail_path = car.xpath('./a/@href').extract_first()
            detail_url = f'https://www.che168.com{detail_path}'
            inf = {'型號': car_name, '里程數': car_journey, '所在地': car_city, '日期': car_buytime, '價格': car_price}

            next_url = response.xpath('//*[@id="listpagination"]/a[@class="page-item-next"]/@href').extract_first()
            if next_url:
                full_nextlink = 'https://www.che168.com/' + next_url
                # 相當與return  callback回撥到哪個函式
                yield scrapy.Request(full_nextlink, callback=self.parse)

            yield scrapy.Request(detail_url, callback=self.parse_detail_page, meta=inf)

    def parse_detail_page(self, response):
        seller_info = re.findall(
            '<span class="manger-name">(.*?)</span>', response.text)[0]
        seller_location = re.findall(
            '<div class="protarit-adress">(.*?)</div>', response.text)[0]
        inf = response.meta
        print(f"{inf},商家/個人資訊:{seller_info},商家/個人詳細地址:{seller_location}")

        這裡重點提幾點:

        response:是Response物件;

        yield :可以將它看作return,但是它可以迴圈;

        callback:回撥函式,將請求到的資訊傳給哪個函式;

        meta:傳遞當前爬取到的頁面資訊(解析過的)。

        執行爬蟲:

                輸入命令scrapy crawl 爬蟲名執行scrapy。

如果報403錯誤,可以開啟setting,在相同的位置新增user_agent。

         由於我寫的爬蟲是爬取網頁上的全部頁資訊,所以有些請求返回了302,在瀏覽器中開啟汽車之家,會發現需要驗證。

         這是因為汽車之家,採取了IP反爬。

        網站運維人員在對日誌進行分析時有時會發現同一時間段內某一個或某幾個IP訪問量特別大,由於爬蟲是通過程式來自動化爬取頁面資訊的,因此其單位時間的請求量較大,且相鄰請求時間間隔較為固定,這時就基本可以判斷此類行為系爬蟲所為,此時即可在伺服器上對異常IP進行封鎖。

        面對這種情況,我們採取的措施:加上代理IP池。

        這裡就不展開說了,有興趣的可先自行搜尋。

(七)爬蟲注意事項

一、Robots協議

        Robots協議(也稱為爬蟲協議、機器人協議等)的全稱是“網路爬蟲排除標準”(Robots ExclusionProtocol),網站通過Robots協議告訴搜尋引擎哪些頁面可以抓取,哪些頁面不能抓取.

        也就是說Robots協議上會說明不可以爬取哪些頁面。在使用scrapy爬取時,scrapy會首先爬取Robots頁。一般該頁在根目錄下的robots.txt中,如百度:https://www.baidu.com/robots.txt

        如果沒有的話,就是允許爬取。

二、遵守Robots協議也有可能犯法

        不一定說遵守Robots就一定沒事,robots協議只是行業約定,並沒有什麼法規強制遵守。

        平時爬取我們需要特別注意3個紅線:

  1. 個人資訊
  2. 商業祕密
  3. 國家祕密

        當然爬取過程中以下2條也需要遵循

  1. 使用爬蟲技術,使對方網站業務無法正常執行。
  2. 使用高併發請求,使對方伺服器持續處於高壓狀態導致其當機的。

        不止這樣,最後對爬取資料的使用也有可能犯法。

        這裡給大家一個政府通知作為參考:國家網際網路資訊辦公室關於《資料安全管理辦法(徵求意見稿)》公開徵求意見的通知-中共中央網路安全和資訊化委員會辦公室 (cac.gov.cn)

(八)反爬與反反爬

        這裡並沒有講什麼處理反爬的措施,我就列一些常見反爬手段,就不細講了。

        反爬手段與處理措施

                headers欄位限制和POST、GET請求引數限制

                        處理措施:

                        加上需要的欄位和引數

                        比較難的就是欄位或者引數是JS檔案動態生成的,這時候就需要我們檢視JS原始檔

                IP限制

                        處理措施:

                        使用代理IP或者建立IP池

                JS加密

                        處理措施:

                        這個主要就是記憶啦,比如:加密後資料最後有等號,就有可能是MD5加密

                        然後在JS原始碼中搜尋關鍵字樣,檢視加密過程,之後使用python復原。

                等

        爬蟲練習專案:crawler_practice.zip-Python文件類資源-CSDN下載

後續

        爬蟲方向:大概就是構建基於scrapy符合公司爬取資料的框架。

        所需知識大概有:

                        熟悉scrapy框架,熟悉各種反爬手段以及處理措施;

                        熟悉MySQL、rides資料庫。

                        上述兩條是這個崗位的核心技能

                        這個崗位需要做到的事情就是爬取資料、篩選資料、儲存資料。

        如果你想要從事這方面的工作,那就朝著這個方向前進吧~~

如內容有誤,還望提醒。

相關文章