1. 程式人生 > >Python爬蟲從入門到放棄(二十四)之 Scrapy登錄知乎

Python爬蟲從入門到放棄(二十四)之 Scrapy登錄知乎

提示 mac 重要 瀏覽器 com fin 傳遞 問題 turn

原文地址https://www.cnblogs.com/zhaof/p/7406482.html

因為現在很多網站為了限制爬蟲,設置了為只有登錄才能看更多的內容,不登錄只能看到部分內容,這也是一種反爬蟲的手段,所以這個文章通過模擬登錄知乎來作為例子,演示如何通過scrapy登錄知乎

在通過scrapy登錄知乎之前,我們先通過requests模塊登錄知乎,來熟悉這個登錄過程

不過在這之前需要了解的知識有:

cookie和session
關於cookie和session我之前整理了一篇博客供參考:
http://www.cnblogs.com/zhaof/p/7211253.html
requests模塊的會話維持功能:
這個我在 http://www.cnblogs.com/zhaof/p/6915127.html 關於requests模塊中也已經做了整理
主要內容如下,詳細內容可參考上面那篇關於requests模塊使用的文章
會話維持


cookie的一個作用就是可以用於模擬登陸,做會話維持

import requests
s = requests.Session()
s.get("http://httpbin.org/cookies/set/number/123456")
response = s.get("http://httpbin.org/cookies")
print(response.text)

這是正確的寫法,而下面的寫法則是錯誤的

import requests
requests.get("http://httpbin.org/cookies/set/number/123456")
response = requests.get("http://httpbin.org/cookies")
print(response.text)

因為這種方式是兩次requests請求之間是獨立的,而第一次則是通過創建一個session對象,兩次請求都通過這個對象訪問
關於爬蟲常見登錄的方法
這裏我之前的文章 http://www.cnblogs.com/zhaof/p/7284312.html 也整理的常用的爬蟲登錄方法
這點是非常重要的

只有上面這些基礎的內容都已經掌握,才能完成下面內容

非框架登錄知乎

這裏我測試的結果是通過爬蟲登錄知乎的時候必須攜帶驗證碼,否則會提示驗證碼錯誤,下面是關於如果沒有帶驗證碼時候提示的錯誤,這個錯誤可能剛開始寫登錄知乎的時候都會碰到,所以這裏我把這段代碼貼出來:

技術分享圖片
import json
import requests
from bs4 import BeautifulSoup

headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
}

#這裏是非常關鍵的
session = requests.session()

def get_index():
    ‘‘‘
    用於獲取知乎首頁的html內容
    :return:
    ‘‘‘
    response = session.get("http://www.zhihu.com",headers=headers)
    return response.text

def get_xsrf():
    ‘‘‘
    用於獲取xsrf值
    :return:
    ‘‘‘
    html = get_index()
    soup = BeautifulSoup(html,‘lxml‘)
    res = soup.find("input",attrs={"name":"_xsrf"}).get("value")
    return res


def zhihu_login(account,password):
    ‘‘‘
    知乎登錄
    :param account:
    :param password:
    :return:
    ‘‘‘
    _xsrf = get_xsrf()
    post_url = "https://www.zhihu.com/login/phone_num"

    post_data = {
        "_xsrf":_xsrf,
        "phone_num":account,
        "password":password,
    }
    response = session.post(post_url,data=post_data,headers=headers)
    res = json.loads(response.text)
    print(res)


zhihu_login(‘13121210484‘,‘********‘)
技術分享圖片

上述代碼當你的用戶名和密碼都正確的時候最後結果會打印如下內容:

技術分享圖片

我猜測是可能知乎識別了這是一個爬蟲,所以讓每次登陸都需要驗證碼,其實這個時候你正常通過瀏覽器登陸知乎並不會讓你輸入驗證碼,所以這裏我們需要獲去驗證碼並將驗證碼傳遞到請求參數中,我們分析登錄頁面就可當登錄頁需要輸入驗證碼的時候,我們點擊驗證碼會生成新的驗證碼,抓包分析如下:

技術分享圖片

技術分享圖片

這行我們就獲得了生成驗證碼的地址:
https://www.zhihu.com/captcha.gif?r=1503303312357&type=login
這個時候我們登錄的時候傳遞的參數中就會增加captcha參數

技術分享圖片

所以我們將上面的代碼進行更改,添加驗證碼參數

技術分享圖片
import json
import requests
from bs4 import BeautifulSoup

headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
}

#這裏是非常關鍵的
session = requests.session()


def get_index():
    ‘‘‘
    用於獲取知乎首頁的html內容
    :return:
    ‘‘‘
    response = session.get("http://www.zhihu.com",headers=headers)
    return response.text

def get_xsrf():
    ‘‘‘
    用於獲取xsrf值
    :return:
    ‘‘‘
    html = get_index()
    soup = BeautifulSoup(html,‘lxml‘)
    res = soup.find("input",attrs={"name":"_xsrf"}).get("value")
    return res


def get_captcha():
    ‘‘‘
    獲取驗證碼圖片
    :return:
    ‘‘‘
    import time
    t = str(int(time.time()*1000))
    captcha_url = "https://www.zhihu.com/captcha.gif?r={0}&type=login".format(t)
    t = session.get(captcha_url,headers=headers)
    with open("captcha.jpg","wb") as f:
        f.write(t.content)

    try:
        from PIL import Image
        im = Image.open("captcha.jpg")
        im.show()
        im.close()
    except:
        pass

    captcha = input("輸入驗證碼>")
    return captcha


def zhihu_login(account,password):
    ‘‘‘
    知乎登錄
    :param account:
    :param password:
    :return:
    ‘‘‘
    _xsrf = get_xsrf()
    post_url = "https://www.zhihu.com/login/phone_num"
    captcha = get_captcha()

    post_data = {
        "_xsrf":_xsrf,
        "phone_num":account,
        "password":password,
        ‘captcha‘:captcha,
    }
    response = session.post(post_url,data=post_data,headers=headers)
    res = json.loads(response.text)
    print(res)


zhihu_login(‘13121210484‘,‘******‘)
技術分享圖片

這樣我們再次登錄就會發現結果如下,表示登錄成功:

技術分享圖片

這裏要說明的一個問題是這裏的驗證碼並沒有接打碼平臺,所以是手工輸入的。

scrapy登錄知乎

我們上面已經通過非框架的模式即requests模塊的方式成功登錄了知乎,現在就是把上面的代碼功能在scrapy中實現,這裏有一個非常重要的地方,上面的代碼中為了會話維持,我們通過:
session = requests.session()
那麽我們如何在scrapy中實現呢?

這裏就是通過yield,完整代碼如下(這裏的爬蟲是在scrapy項目裏直接生成的一個爬蟲):

技術分享圖片
import json
import re

import scrapy
from urllib import parse

class ZhihuSpider(scrapy.Spider):
    name = "zhihu"
    allowed_domains = ["www.zhihu.com"]
    start_urls = [‘https://www.zhihu.com/‘]
    headers = {
        ‘User-Agent‘:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",

    }

    def start_requests(self):
        ‘‘‘
        重寫start_requests,請求登錄頁面
        :return:
        ‘‘‘
        return [scrapy.Request(‘https://www.zhihu.com/#signin‘,headers=self.headers,callback=self.login)]


    def login(self,response):
        ‘‘‘
        先通過正則獲取xsrf值,然後通過scrapy.Request請求驗證頁面獲取驗證碼
        :param response:
        :return:
        ‘‘‘
        response_text = response.text
        match_obj = re.match(‘.*name="_xsrf" value="(.*?)"‘,response_text,re.DOTALL)
        print(match_obj.group(1))
        xsrf=‘‘
        if match_obj:
            xsrf = match_obj.group(1)
        if xsrf:
            post_data = {
                "_xsrf":xsrf,
                "phone_num":"13121210484",
                "password":"********",
                ‘captcha‘:‘‘,
            }
            import time
            t = str(int(time.time() * 1000))
            captcha_url = "https://www.zhihu.com/captcha.gif?r={0}&type=login".format(t)
            #這裏利用meta講post_data傳遞到後面的response中
            yield scrapy.Request(captcha_url,headers=self.headers,meta={"post_data":post_data} ,callback=self.login_after_captcha)

    def login_after_captcha(self,response):
        ‘‘‘
        將驗證碼寫入到文件中,然後登錄
        :param response:
        :return:
        ‘‘‘
        with open("captcha.jpg",‘wb‘) as f:
            f.write(response.body)
        try:
            from PIL import Image
            im = Image.open("captcha.jpg")
            im.show()

        except:
            pass
        #提示用戶輸入驗證碼
        captcha = input("請輸入驗證碼>:").strip()
        #從response中的meta中獲取post_data並賦值驗證碼信息
        post_data = response.meta.get("post_data")
        post_data["captcha"] = captcha
        post_url = "https://www.zhihu.com/login/phone_num"
        # 這裏是通過scrapy.FormRequest提交form表單
        return [scrapy.FormRequest(
            url=post_url,
            formdata=post_data,
            headers=self.headers,
            callback=self.check_login,
        )]

    def check_login(self,response):
        ‘‘‘
        驗證服務器的返回數據判斷是否成功,我們使用scrapy會自動攜帶我們登錄後的cookie
        :param response:
        :return:
        ‘‘‘
        text_json = json.loads(response.text)
        print(text_json)
        for url in self.start_urls:
            yield self.make_requests_from_url(url,dont_filter=True,header=self.headers)
技術分享圖片

上述代碼中:

yield scrapy.Request(captcha_url,headers=self.headers,meta={"post_data":post_data} ,callback=self.login_after_captcha)

原本scrapy中的scrapy.Request會保存訪問過程中的cookie信息其實這裏面也是用也是cookiejar,這裏通過yield 的方式實現了與會話的維持
我們通過調試登錄,如下,同樣也登錄成功:

技術分享圖片

Python爬蟲從入門到放棄(二十四)之 Scrapy登錄知乎