第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁(yè) 慕課教程 Scrapy 入門教程 Scrapy 入門教程 Scrapy 抓取起點(diǎn)中文網(wǎng):實(shí)現(xiàn)登錄和認(rèn)證

Scrapy 抓取起點(diǎn)中文網(wǎng)(上):實(shí)現(xiàn)登錄和認(rèn)證

本小節(jié)將完成一個(gè)有趣的 Scrapy 爬蟲項(xiàng)目,主要實(shí)現(xiàn)的主要功能如下:

  • 實(shí)現(xiàn)網(wǎng)站登錄,得到相應(yīng)的個(gè)人信息,比如用戶信息,我的書架等,并實(shí)現(xiàn)一鍵刪除書架上所有書籍的功能;

本節(jié)和接下來的一節(jié)都是實(shí)戰(zhàn)環(huán)節(jié),用于梳理 Scrapy 爬蟲框架的基本使用,也為鞏固和加深前面所學(xué)知識(shí)。

如果是想基于基本的 API 方式登錄,我們會(huì)面臨兩大難點(diǎn):

  • 手機(jī)驗(yàn)證碼校驗(yàn) ,如下圖所示:
    圖片描述
起點(diǎn)網(wǎng)站登錄手機(jī)發(fā)送驗(yàn)證碼
  • 滑動(dòng)驗(yàn)證碼校驗(yàn),如下圖所示:

圖片描述

起點(diǎn)網(wǎng)站登錄滑動(dòng)驗(yàn)證碼

繞過這些校驗(yàn)的方法超過了本教程的知識(shí)范圍,故我們不再次詳細(xì)討論。好在起點(diǎn)網(wǎng)支持自動(dòng)登錄過程,也就是 Cookie 登錄:

圖片描述

起點(diǎn)網(wǎng)支持自動(dòng)登錄

第一次手動(dòng)登錄起點(diǎn),選擇自動(dòng)登錄后,起點(diǎn)網(wǎng)站返回的 Cookie 信息就會(huì)保存至本地。下次再訪問起點(diǎn)網(wǎng)時(shí),通過請(qǐng)求帶上該 Cookie 信息就能正確識(shí)別用戶,實(shí)現(xiàn)自動(dòng)登錄過程。Cookie 存在本地,就存在被代碼讀取的可能。通常而言,我們來使用 Python 中的 browsercookie 庫(kù)可以獲取瀏覽器的 cookie,目前它只支持 Chrome 和 FireFox 兩種瀏覽器。不過對(duì)于 Chrome 80.X 版本的瀏覽器,其中的 cookie 信息被加密了,我們無法按照早期的操作進(jìn)行 cookie 讀取。

不過網(wǎng)上這個(gè)博客給出了一個(gè)解密 Cookie 的代碼,我們拿過來簡(jiǎn)單改造下,做成一個(gè)輔助模塊:

# 參考文檔:https://blog.csdn.net/u012552769/article/details/105001108

import sqlite3
import urllib3
import os
import json

import sys
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes


urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


def dpapi_decrypt(encrypted):
    import ctypes
    import ctypes.wintypes

    class DATA_BLOB(ctypes.Structure):
        _fields_ = [('cbData', ctypes.wintypes.DWORD),
                    ('pbData', ctypes.POINTER(ctypes.c_char))]

    p = ctypes.create_string_buffer(encrypted, len(encrypted))
    blobin = DATA_BLOB(ctypes.sizeof(p), p)
    blobout = DATA_BLOB()
    retval = ctypes.windll.crypt32.CryptUnprotectData(
        ctypes.byref(blobin), None, None, None, None, 0, ctypes.byref(blobout))
    if not retval:
        raise ctypes.WinError()
    result = ctypes.string_at(blobout.pbData, blobout.cbData)
    ctypes.windll.kernel32.LocalFree(blobout.pbData)
    return result


def aes_decrypt(encrypted_txt):
    with open(os.path.join(os.environ['LOCALAPPDATA'],
                           r"Google\Chrome\User Data\Local State"), encoding='utf-8', mode="r") as f:
        jsn = json.loads(str(f.readline()))
    encoded_key = jsn["os_crypt"]["encrypted_key"]
    encrypted_key = base64.b64decode(encoded_key.encode())
    encrypted_key = encrypted_key[5:]
    key = dpapi_decrypt(encrypted_key)
    nonce = encrypted_txt[3:15]
    cipher = Cipher(algorithms.AES(key), None, backend=default_backend())
    cipher.mode = modes.GCM(nonce)
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted_txt[15:])


def chrome_decrypt(encrypted_txt):
    if sys.platform == 'win32':
        try:
            if encrypted_txt[:4] == b'x01x00x00x00':
                decrypted_txt = dpapi_decrypt(encrypted_txt)
                return decrypted_txt.decode()
            elif encrypted_txt[:3] == b'v10':
                decrypted_txt = aes_decrypt(encrypted_txt)
                return decrypted_txt[:-16].decode()
        except WindowsError:
            return None
    else:
        raise WindowsError


def get_cookies_from_chrome(domain, key_list):
    sql = f'SELECT name, encrypted_value as value FROM cookies where host_key like "%{domain}%"'
    filename = os.path.join(os.environ['USERPROFILE'], r'AppData\Local\Google\Chrome\User Data\default\Cookies')
    con = sqlite3.connect(filename)
    con.row_factory = sqlite3.Row
    cur = con.cursor()
    cur.execute(sql)
    cookie_dict = {}
    for row in cur:
        if row['value'] is not None:
            name = row['name']
            value = chrome_decrypt(row['value'])
            if value is not None and name in key_list:
                cookie_dict[name] = value
    return cookie_dict

Tips:上述這段代碼不用糾結(jié)細(xì)節(jié),前面函數(shù)的主要是替 get_cookies_from_chrome() 函數(shù)服務(wù)的,而該函數(shù)的輸入要搜索的網(wǎng)站以及提取相應(yīng)網(wǎng)站 cookie 信息中的某個(gè)具體字段,返回相應(yīng)的結(jié)果。

本人 Python 3.8.2 安裝的是 win32 版本,該段代碼親測(cè)有效。來看看起點(diǎn)中文網(wǎng)給讀者生成的 cookie 數(shù)據(jù),我們調(diào)用上面的獲取 cookie 信息的代碼來從中提取相應(yīng)數(shù)據(jù):

圖片描述

起點(diǎn)的cookie信息
print(get_cookies_from_chrome('qidian.com', '_csrfToken'))
print(get_cookies_from_chrome('qidian.com', 'e1'))
print(get_cookies_from_chrome('qidian.com', 'e2'))

執(zhí)行上述代碼我們可以得到如下結(jié)果:

PS C:\Users\spyinx> & "D:/Program Files (x86)/python3/python.exe" c:/Users/spyinx/Desktop/test_cookie.py
{'_csrfToken': 'YJklLmhMNpEfuSmqZZGaK72D4sUVJty52gyKwXXX'}
{'e1': '%7B%22pid%22%3A%22qd_p_qidian%22%2C%22eid%22%3A%22qd_A08%22%2C%22l1%22%3A1%7D'}
{'e2': '%7B%22pid%22%3A%22qd_p_qidian%22%2C%22eid%22%3A%22qd_A10%22%2C%22l1%22%3A1%7D'}

這說明我們通過前面的代碼能爭(zhēng)取獲取到起點(diǎn)網(wǎng)保存在 Chrome 瀏覽器中的 cookie 信息。因此,前面的代碼將作為我們讀取起點(diǎn)用戶登錄 Cookie 的重要輔助模塊。

Tips:這個(gè)測(cè)試只能在裝了 Chrome 瀏覽器的 Windows 系統(tǒng)上進(jìn)行測(cè)試,或者是 Linux 的桌面版

我們首先來創(chuàng)建一個(gè)起點(diǎn)爬蟲:

PS C:\Users\Administrator\Desktop> scrapy startproject qidian_spider

接下里我們來看看要提取的我的書架的信息:

圖片描述

我的書架書籍信息

對(duì)應(yīng)的 items.py 的內(nèi)容如下:

# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class QidianSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 分組
    category = scrapy.Field()
    # 小說名
    name = scrapy.Field()
    # 最新章節(jié)
    latest_chapter = scrapy.Field()
    # 作者
    author = scrapy.Field()
    # 更新時(shí)間
    update_time = scrapy.Field()
    # 閱讀進(jìn)度
    progress_status = scrapy.Field()

接下來,在爬蟲部分需要請(qǐng)求該頁(yè)面然后提取相應(yīng)的數(shù)據(jù),我們的爬蟲代碼如下:

"""
獲取用戶書架數(shù)據(jù)
"""
import json
from urllib import parse

from scrapy import Request
from scrapy.spiders import Spider

from .get_cookie import get_cookies_from_chrome
from ..items import QidianSpiderItem


class BookCaseSpider(Spider):
    name = "bookcase"
    # 構(gòu)造函數(shù)
    def __init__(self):
        # 最重要的就是這個(gè)獲取起點(diǎn)的cookie數(shù)據(jù)了,這里保存了之前用戶登錄的cookie信息
        self.cookie_dict = get_cookies_from_chrome(
            "qidian.com", 
            ["_csrfToken", "e1", "e2", "newstatisticUUID", "ywguid", "ywkey"]
        )

    def start_requests(self):
        url = "https://my.qidian.com/bookcase"
        # http請(qǐng)求時(shí)附加上cookie信息
        yield Request(url=url, cookies=self.cookie_dict)

    def parse(self, response):
        item = QidianSpiderItem()
        books = response.xpath('//table[@id="shelfTable"]/tbody/tr')
        for book in books:
            category = book.xpath('td[@class="col2"]/span/b[1]/a[1]/text()').extract_first()
            name = book.xpath('td[@class="col2"]/span/b[1]/a[2]/text()').extract_first()
            latest_chapter = book.xpath('td[@class="col2"]/span/a/text()').extract_first()
            update_time = book.xpath('td[3]/text()').extract_first()
            author = book.xpath('td[@class="col4"]/a/text()').extract_first()
            progress_status = book.xpath('td[@class="col5"]/a/text()').extract_first()

            item['category'] = category
            item['name'] = name
            item['latest_chapter'] = latest_chapter
            item['update_time'] = update_time
            item['author'] = author
            item['progress_status'] = progress_status

            print(f'get item = {item}')

            yield item

最重要的方法就是那個(gè)獲取 cookie 信息的方法了,正是靠著這個(gè) cookie,我們才能獲取相應(yīng)的用戶書架的網(wǎng)頁(yè)并提取相應(yīng)的書籍信息。接下來,簡(jiǎn)單實(shí)現(xiàn)一個(gè) item pipeline 用于保存書架上的書籍信息,該代碼位于 scrapy/pipelines.py 文件中,默認(rèn)的 pipelines 都寫會(huì)在這里:

# 源碼位置:scrapy/pipelines.py

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html

import json
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class QidianSpiderPipeline:
    def open_spider(self, spider):
        self.file = open("bookcase.json", 'w+', encoding='utf-8')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        data = json.dumps(dict(item), ensure_ascii=False)
        self.file.write(f"{data}\n")
        return item

最后別忘了在 settings.py 中添加這個(gè) item pipeline:

ITEM_PIPELINES = {
    'qidian_spider.pipelines.QidianSpiderPipeline': 300,
}

我們運(yùn)行下這個(gè)爬蟲,看看是否能抓到我們想要的數(shù)據(jù):

PS C:\Users\Administrator\Desktop> scrapy crawl bookcase

最后的結(jié)果如下:

圖片描述

獲取用戶的書架上書籍信息

這樣,我們就成功實(shí)現(xiàn)了用戶登錄后的訪問動(dòng)作。接下來我們?cè)谶@個(gè)基礎(chǔ)上進(jìn)一步擴(kuò)展,實(shí)現(xiàn)清除書架上所有的書籍,類似于淘寶的一鍵清除購(gòu)物車。

2. 刪除起點(diǎn)網(wǎng)用戶的所有書架

首先我們隨便添加一個(gè)書籍到書架上,然后進(jìn)行清楚,請(qǐng)看下圖,通過 Chrome 開發(fā)者工具我們可以找到刪除書架上書籍的 URL 請(qǐng)求以及相應(yīng)攜帶參數(shù):

圖片描述

刪除書架上的書籍

該請(qǐng)求一共有三個(gè)參數(shù):

  • _csrfToken:可以從 cookie 中獲??;
  • bids:書籍編號(hào),可以從這一行的 html 元素中提??;
  • gid:發(fā)現(xiàn)是固定的100;

于是我們?cè)谡?qǐng)求到書架上的書籍信息時(shí),解析得到書籍編號(hào),然后對(duì)應(yīng)發(fā)送刪除該書籍的請(qǐng)求,對(duì)應(yīng)的代碼如下:

from .get_cookie import get_cookies_from_chrome
from ..items import QidianSpiderItem

# 刪除書籍信息 https://my.qidian.com/ajax/BookShelf/DelBook?_csrfToken=YJklLmhEFpEfuSmqZZGaK72D4sUVJty52gyKw0TJ&bids=1022282526&gid=-100


class BookCaseSpider(Spider):
    name = "bookcase"
    # 構(gòu)造函數(shù)
    def __init__(self):
        self.cookie_dict = get_cookies_from_chrome(
            "qidian.com", 
            ["_csrfToken", "e1", "e2", "newstatisticUUID", "ywguid", "ywkey"]
        )

    def start_requests(self):
        url = "https://my.qidian.com/bookcase"
        # http請(qǐng)求時(shí)附加上cookie信息
        yield Request(url=url, cookies=self.cookie_dict)

    def parse(self, response):
        item = QidianSpiderItem()
        books = response.xpath('//table[@id="shelfTable"]/tbody/tr')
        for book in books:
            # ...
            
            # 刪除該書籍信息
            query_data = {
                'bids': book.xpath('td[6]/div[@class="ui-datalist"]/div[@class="ui-datalist-datalist"]/a[1]/@data-id').extract_first(),
                'gid': '-100',
                '_csrfToken': self.cookie_dict['_csrfToken']
            }
            url = "https://my.qidian.com/ajax/BookShelf/DelBook?{}".format(parse.urlencode(query_data))
            print('對(duì)應(yīng)刪除url請(qǐng)求={}'.format(url))
            yield Request(url=url, method='get', cookies=self.cookie_dict, callback=self.parse_delete_book)


    def parse_delete_book(self, response):
        """
        刪除結(jié)果:{"code":0,"data":{"1022354901":{"code":0,"message":"操作成功"}},"msg":"成功"}
        """
        data = response.text
        print('刪除響應(yīng):{}'.format(data))
        if isinstance(data, str):
            data = json.loads(data)

        print('msg = {}'.format(data['msg']))

是不是非常簡(jiǎn)單?來看看最后運(yùn)行的效果:

圖片描述

啟動(dòng)清除書架信息爬蟲

圖片描述

最后用戶書架數(shù)據(jù)

是不是很有意思?基于這樣的操作,我們想想淘寶一鍵清除購(gòu)物車功能,是不是也能這樣實(shí)現(xiàn)?還有每次明星的戀情有變,連夜刪除上千條微博,導(dǎo)致手指酸痛,我們是否能提供一鍵清除微博的功能,解決他們的痛點(diǎn)?這些事情是不是想想就很激動(dòng)?還等什么,心動(dòng)不如行動(dòng),這個(gè)就作為課后作業(yè)吧,希望你能獨(dú)立完成淘寶的一鍵清除購(gòu)物車代碼。

3. 小結(jié)

本節(jié)中,我們基于 Scrapy 框架講解了下如何簡(jiǎn)單地實(shí)現(xiàn)自動(dòng)登錄操作,避開滑動(dòng)驗(yàn)證碼的二次驗(yàn)證。然后,在登錄的基礎(chǔ)上實(shí)現(xiàn)刪除用戶的所有書架操作。