Python之爬蟲(二十六) Scrapy登陸知乎

由於如今不少網站爲了限制爬蟲,設置了爲只有登陸才能看更多的內容,不登陸只能看到部份內容,這也是一種反爬蟲的手段,因此這個文章經過模擬登陸知乎來做爲例子,演示如何經過scrapy登陸知乎html

在經過scrapy登陸知乎以前,咱們先經過requests模塊登陸知乎,來熟悉這個登陸過程json

不過在這以前須要瞭解的知識有:

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 也整理的經常使用的爬蟲登陸方法
這點是很是重要的
cookie

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

非框架登陸知乎

這裏我測試的結果是經過爬蟲登陸知乎的時候必須攜帶驗證碼,不然會提示驗證碼錯誤,下面是關於若是沒有帶驗證碼時候提示的錯誤,這個錯誤可能剛開始寫登陸知乎的時候都會碰到,因此這裏我把這段代碼貼出來:框架

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','********')

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

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

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

因此咱們將上面的代碼進行更改,添加驗證碼參數

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 的方式實現了與會話的維持
咱們經過調試登陸,以下,一樣也登陸成功:

相關文章
相關標籤/搜索