【作者主頁(yè)】:吳秋霖
【作者介紹】:Python領(lǐng)域優(yōu)質(zhì)創(chuàng)作者、阿里云博客專家、華為云享專家。長(zhǎng)期致力于Python與爬蟲領(lǐng)域研究與開(kāi)發(fā)工作!
【作者推薦】:對(duì)JS逆向感興趣的朋友可以關(guān)注《爬蟲JS逆向?qū)崙?zhàn)》,對(duì)分布式爬蟲平臺(tái)感興趣的朋友可以關(guān)注《分布式爬蟲平臺(tái)搭建與開(kāi)發(fā)實(shí)戰(zhàn)》
還有未來(lái)會(huì)持續(xù)更新的驗(yàn)證碼突防、APP逆向、Python領(lǐng)域等一系列文章
1. 寫在前面
??除了對(duì)x-s、x-s-common進(jìn)行分析實(shí)現(xiàn)加密算法,還有之前文章中提到的通過(guò)JS注入免扣加密算法的方式獲取加密參數(shù)
加密分析及算法文章請(qǐng)閱讀這篇文章:x-s、x-s-common加密分析(2024-01-10更新)
x-s的加密算法為JS實(shí)現(xiàn)、x-s-common的加密算法為Python實(shí)現(xiàn)
2. 分析加密入口
可以看到上圖斷點(diǎn)處l包含x-s跟x-t的返回,看下面這行代碼:
l = (a && void 0 !== window._webmsxyw ? window._webmsxyw : encrypt_sign)(s, i) || {};
window._webmsxyw函數(shù)內(nèi)即加密邏輯,在自執(zhí)行函數(shù)內(nèi)部并添加在了window屬性中
該函數(shù)接受兩個(gè)參數(shù),s是api接口的路徑,i是請(qǐng)求提交的參數(shù)
3. 使用JS注入
可以使用Playwright或者pyppeteer實(shí)現(xiàn),通過(guò)瀏覽器的JavaScript注入來(lái)獲取加密參數(shù),代碼實(shí)現(xiàn)分別如下
Playwright方式:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
page = await browser.new_page()
# 注入stealth.min.js腳本
await page.add_init_script(path="stealth.min.js")
url = "" # 請(qǐng)求api
data = "" # 請(qǐng)求參數(shù)
# 執(zhí)行JavaScript
encrypt_params = await page.evaluate('([url, data]) => window._webmsxyw(url, data)', [url, data])
local_storage = await page.evaluate('() => window.localStorage')
print(encrypt_params)
print(local_storage)
await browser.close()
asyncio.run(main())
pyppeteer方式:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=True)
page = await browser.newPage()
# 注入stealth.min.js腳本
stealth_script = open("stealth.min.js", "r").read()
await page.evaluateOnNewDocument(stealth_script)
url = "" # 請(qǐng)求api
data = "" # 請(qǐng)求參數(shù)
# 執(zhí)行JavaScript
encrypt_params = await page.evaluate('([url, data]) => window._webmsxyw(url, data)', [url, data])
local_storage = await page.evaluate('() => window.localStorage')
print(encrypt_params)
print(local_storage)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
上面的stealth.min.js腳本注入的作用是為了防止被檢測(cè)的,另外cookie參數(shù)需要設(shè)置屬性來(lái)避免Web端出現(xiàn)滑動(dòng)驗(yàn)證碼
當(dāng)然,這個(gè)都是爬蟲最終工程化需要考慮的事情,這里主要還是通過(guò)非逆向分析的方式去解決加密參數(shù)問(wèn)題!
window.localStorage在之前加密分析的文章中已經(jīng)詳細(xì)介紹了,localStorage是一個(gè)在瀏覽器中存儲(chǔ)鍵值對(duì)的API,通常用于持久化地存儲(chǔ)數(shù)據(jù),所需的b1參數(shù)就在其中
JS注入方式運(yùn)行結(jié)果如下所示:
x-s跟x-t的加密參數(shù)通過(guò)注入的方式能夠直接拿到,但是x-s-common的參數(shù)仍需要通過(guò)sign的方法加密計(jì)算生成!
Python版本的sign加密算法在之前的加密分析文章中已提供!JS注入的方式主要為了獲取這些個(gè)參數(shù):x-s、x-t、b1
JS注入的方式對(duì)于有前端基礎(chǔ)及經(jīng)驗(yàn)的小伙伴,就很簡(jiǎn)單了。通過(guò)上面的方式獲取到所有的加密參數(shù)后,接下來(lái)就是爬蟲的工程化
4. 爬蟲工程化
以筆記搜索為例,爬蟲代碼實(shí)現(xiàn)如下:
import json
import httpx
from typing import Dict, Optional
async def request(self, method, url, **kwargs) -> Dict:
async with httpx.AsyncClient(proxies=self.proxies) as client:
response = await client.request(
method, url, timeout=self.timeout,
**kwargs
)
data: Dict = response.json()
if data["success"]:
return data.get("data", data.get("success", {}))
elif data["code"] == self.IP_ERROR_CODE:
raise IPBlockError(self.IP_ERROR_STR)
else:
raise DataFetchError(data.get("msg", None))
async def unified_request(self,
uri: Optional[str] = None,
data: Optional[dict] = None,
keyword: Optional[str] = None,
page: Optional[int] = 1,
page_size: Optional[int] = 20,
sort: Optional[SearchSortType] = SearchSortType.GENERAL,
note_type: Optional[SearchNoteType] = SearchNoteType.ALL) -> Dict:
if keyword:
_host = "https://edith.xiaohongshu.com"
uri = "/api/sns/web/v1/search/notes"
data = {
"keyword": keyword,
"page": page,
"page_size": page_size,
"search_id": get_search_id(),
"sort": sort.value,
"note_type": note_type.value
}
elif uri and data:
headers = await self._pre_headers(uri, data)
json_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
return await self.request(method="POST", url=f"{self._host}{uri}",
data=json_str, headers=headers)
else:
raise ValueError("Either 'uri' and 'data' or 'keyword' must be provided.")
return await request(method="POST", url=f"{_host}{uri}", data=json.dumps(data), headers=await self._pre_headers(uri, data))
最后,訂閱的小伙伴可找作者獲取開(kāi)箱即用的完整爬蟲項(xiàng)目代碼,如下:
JS注入方式筆記搜索:
JS注入方式筆記評(píng)論:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-793507.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-793507.html
到了這里,關(guān)于使用Python爬取小紅書筆記與評(píng)論(僅供學(xué)習(xí)交流)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!