實戰記錄:爬大眾點評評論區
近日,小伙伴的女票要寫論文,需要點數據。
借此機會,我也實戰了一把。着實有趣。
迭代日志:
2021-01-09:
- 抓取完一個頁面停止 3 秒鍾
- 添加自動切換解密方式
- 修正方式 a 解密失敗問題
需求:

技術選型
本身我自己是搞 java 的。剛開始想用 java 來搞,后來想想,有此機會,正好看看py功底如何。所以最終選擇了 Python
分析:
先打開其中一個地址看看都啥東西

哎我去,這是什么玩意?
遇到事情不要慌,先拿出手機發個朋友圈。。
不是,仔細看看。

右邊藏了個
background-image: url(//s3plus.meituan.net/v1/mss_0a06a47…/svgtextcss/32203aa….svg);
還有個
.oyep4y {
background: -14.0px -97.0px;
}
得,拼成完整 URL 打開看看啥東西吧。
這就有點意思了。

經過我兩個夜晚的熬戰,嘖嘖。終於通曉了其中奧秘。

分析其中關系
經過幾輪數據測試。最終發現,少了一個元素,文治就是隱藏在 style 里的font-size。

不要問我為啥知道,經驗使然,其實就是瞎猜-v-列方程去哈哈
又搞了幾輪測試,基本上確定了。
.oyep4y {
background: -apx -bpx;
}
- 先找到樣式中的 b 的值 即 -97 絕對值一下為 97
- 根據
<path xmlns="http://www.w3.org/2000/svg" id="3" d="M0 120 H600"/>中d 的第二個值,找 97 小於的第一個 path.id 即 3 (79 < 97 < 120 ) - 根據 3 找
<textPath xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#3" textLength="476">汗期火奉銳隆播扣患漲醋盈捕離忌脅地淘七潤為精蟲浙躺捏惹作搏謹遼你斬狸</textPath>中的 xlink:href=“#3” - 計算 a/fontSize = 1即第二個字(下標從 0 開始- -)。
同理,找一下男票的男,其a = 504 b= 2210 對應的 pathId = 55
504/14=36
<textPath xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#55" textLength="518">堵隨罰窮麗累源嚷型榴漢很似戴陡棗絕了疼那蓄入朱鑼式愛斃版她根扇牢鳳盆拖酸男</textPath>
查吧。第 36 個字
附上地址。一個 svg 背景中都是字
nice,就是這個規律。找到規律了,就要擼代碼了。里面坑還真不少。
坑一:反爬機制導致不間斷的報 403-。-

這種行為,不得以讓我選擇先爬頁面,后解析。因為我是邊爬邊寫的代碼。。。。
坑二:分析時是這樣的,爬下來的不是
這個怎么說呢。就是上面的分析是對的,但是爬下來的頁面不是這樣的。而是這樣的

但是基本上是一個意思,這種反而簡單了。
爬到的頁面:

其中這個 7kb 的就是錯誤的頁面 403 了
寫入 excel 結果:

目錄結構:
這里我做了一下傻瓜式操作,為了就是方便我的小伙伴簡單使用。所以是這樣的
├── README.md
├── main.py --------------------入口
├── spider_parse ---------------解析相關的
│ └── __init__.py
└── sprider_network ------------爬頁面相關的
└── __init__.py
核心代碼:
main.py
# 抓取模塊
import sprider_network
# 解析模塊
import spider_parse
# 把爬到的數據寫到一個 Excel 表里。也就是 sheet 名為 "湖錦" 后續跟店名替換即可
excel_sheet_name = "湖錦"
if __name__ == '__main__':
menu_choose = input("請輸入菜單序號: 1. 爬頁面 2. 解析 html 文件 3. 抓取未抓取到的頁面: ")
if menu_choose == "1":
# 抓取頁面
page_path = input("請輸入待抓取頁面的 URL(只寫到 p 前面的即可,如http://www.dianping.com/shop/k8tlcPTSvTPl1zUz/review_all/p):")
page_num = input("請輸入要抓取的頁面數量(如要抓取100頁,則輸入100):")
cookies = input("請輸入 COOKIE(防封):")
sprider_network.spider_html(page_path, page_num, cookies)
elif menu_choose == "2":
# 解析html 文件 默認只解析文件大小大於 100KB 的文件
dir_path = \
input("請輸入抓取的頁面所在的路徑:")
# "/Users/yunxuan/PycharmProjects/spider/spider_result/紅鼎豆撈/sources"
spider_parse.load_dir(dir_path)
else:
# 防止一次沒抓完,有403/404 問題
page_path = input("請輸入待抓取頁面的 URL(只寫到 p 前面的即可,如http://www.dianping.com/shop/k8tlcPTSvTPl1zUz/review_all/p):")
page_num = input("請輸入要抓取的頁碼,以英文逗號分開,如:1,3,8:")
cookies = input("請輸入 COOKIE(防封):")
page_number = page_num.split(",")
for i in range(0, len(page_number)):
sprider_network.spider_html(page_path, page_num, cookies)
抓頁面:
# 瀏覽器標識
from fake_useragent import UserAgent
# 請求
import requests
# 文件讀寫
import os
import time
# 初始化一個瀏覽器標識對象
ua = UserAgent()
# 抓取頁面的請求頭信息
request_pages_headers = {
'User-Agent': ua.random,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Host": "www.dianping.com",
"Connection": "keep-alive",
"Cache-Control": "max-age=0",
"Accept-Language": "zh-CN,zh;q=0.9,fr;q=0.8",
"Accept-Encoding": "gzip, deflate",
}
def spider_html(url, page_num, cookie):
"""
抓取 URL 頁面
:param url: 待抓取的 URL 前綴,不含頁碼
:param page_num: 待抓取的頁面數量
:param cookie: 登陸后的 COOKIE
:return: 沒返回的
"""
# 給字典添加 Cookie
request_pages_headers["Cookie"] = cookie
# 頁碼字符串轉成數字
page_num_int = int(page_num)
# 不做過多判斷了,畢竟不是商業化。只是方便使用的,不會隨便傳參數- -
# 定義一個錯誤頁面數組
error_page = []
for i in range(0, page_num_int):
current_page_number = str(i + 1)
print("正在抓取第[" + current_page_number + "]張頁面")
# 拼裝待抓取的 URL 全路徑
request_url = url + str(i)
# 模擬打開瀏覽器,輸入路徑
html = requests.get(request_url, headers=request_pages_headers)
if html.status_code != 200:
print("第[" + current_page_number + "]張頁面抓取失敗,請記錄序號,后續使用 3 菜單重新抓取")
error_page.append(current_page_number)
else:
# 抓取成功的,寫入文件
html_file_name = "page(" + current_page_number + ").html"
# 聲明一個目錄
dir_path = os.path.abspath("spider_result")
# 判斷是否存在
is_exists = os.path.exists(dir_path)
if not is_exists:
# 不存在 創建目錄
os.makedirs(dir_path)
# 存在 直接寫入
with open(dir_path + "/" + html_file_name, "w", encoding="UTF-8") as f:
f.write(html.text)
f.close()
print("第[" + current_page_number + "]張頁面已保存在" + dir_path)
time.sleep(3)
# 錯誤頁面信息寫入日志
dir_path = os.path.abspath("log")
is_exists = os.path.exists(dir_path)
if not is_exists:
# 不存在 創建目錄
os.makedirs(dir_path)
with open(dir_path + "/error.log", "w", encoding="UTF-8") as f:
f.write(str(error_page))
f.close()
print("錯誤頁面已保存至 error.log 文件中,路徑:", dir_path)
print("休息完畢,繼續工作")
頁面解析:
import os
import xlwt
import re
import requests
from pyquery import PyQuery as pq
import datetime
from fake_useragent import UserAgent
ua = UserAgent()
css_header = {
"Accept": "text/k8tlcPTSvTPl1zUz,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate",
'Accept-Language': 'zh-CN,zh;q=0.9,fr;q=0.8',
'Connection': 'keep-alive',
"Host": "s3plus.meituan.net",
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': ua.random,
}
def load_dir(dir_path):
"""
加載目錄
:param dir_path: html 文件存放目錄
:return: 不返回了吧
"""
# 對目錄下文件進行一個排序操作
file_paths = sorted(os.listdir(dir_path))
# 定義一下有多少個文件
file_count = len(file_paths)
# 只抓取2019-1.1 到 2021-1-1 即 2018-12-31 23:59 to 2021-01-01 00:00
# 定義一個 Excel 文件
workbook = xlwt.Workbook(encoding='utf-8')
# 添加一個工作表
worksheet = workbook.add_sheet("未命名工作表")
# 0-10 第 0 行 0 - 10 列。 當做表名
worksheet.write(0, 0, label='評論時間')
worksheet.write(0, 1, label='用戶名')
worksheet.write(0, 2, label='評論內容')
worksheet.write(0, 3, label='評論字數')
worksheet.write(0, 4, label='圖片數量')
worksheet.write(0, 5, label='有無視頻')
worksheet.write(0, 6, label='評分總分')
worksheet.write(0, 7, label='評論者等級')
worksheet.write(0, 8, label='評論下的評論數')
worksheet.write(0, 9, label='點贊數')
worksheet.write(0, 10, label='用戶描述')
# 定義一個行數,用來寫到 Excel 文件對應位置中
row_index = 1
for i in range(0, file_count):
if row_index == -1:
# 結束了,別解析了
break
# 解析文件
row_index = parse_html_file(dir_path + "/" + file_paths[i], worksheet, row_index)
workbook.save(dir_path + "/result.xlsx")
def parse_html_file(file_path, worksheet, row_index):
"""
解析 html 文件
:param file_path: 文件路徑
:param worksheet: Excel 文件對象
:param row_index: Excel 的行號 從第 1 行開始,向下寫
:return:
row_index: 是否結束解析標志 -1 結束解析
"""
# 讀取文件內容到ret 變量中
ret = open(file_path).read()
# 使用選擇器把文本轉成對象
doc = pq(ret)
# 找頁面中的評論區
comments_area = doc("div.reviews-items > ul > li").items()
for data in comments_area:
# 解析 css
dict_svg_text, list_svg_y, dict_css_x_y, is_continue = parse_css(ret)
if not is_continue:
# css 樣式解析失敗,沒必要再解析html 頁面了。解析出來也是錯誤的
print("加密 css 樣式解析失敗:", file_path)
return row_index
# 用戶名
user_name = data("div.main-review > div.dper-info > a").text()
# 用戶評分星級[10-50]
start_shop = str(data("div.review-rank > span").attr("class")).split(" ")[1].replace("sml-str", "")
# 用戶描述:機器:非常好 環境:非常好 服務:非常好 人均:0元
describe_shop = data("div.review-rank > span.score").text()
# 關鍵部分,評論HTML,待處理,評論包含隱藏部分和直接展示部分,默認從隱藏部分獲取數據,沒有則取默認部分。(查看更多)
comments = data("div.review-words.Hide").html()
try:
len(comments)
except:
# 展開評價
comments = data("div.review-words").html()
# 圖片數量
pictures = data("div.main-review > div.review-pictures > ul > li > a > img")
# 數量
pic_num = pictures.length
pic_link = str(pic_num) + "張:\n"
# 鏈接
for pic in pictures.items():
pic_link += (str(pic.attr('data-big')) + "\n")
# 評論點贊數
comments_click_goods_num_wrapper = data("div.main-review > div.misc-info.clearfix > span.actions")
comments_click_goods_number_wrapper_children = comments_click_goods_num_wrapper.children()
comments_goods_num = comments_click_goods_number_wrapper_children.text().replace("(", "").replace(")", "")
# 發表評論的時間
comments_time = data("div.main-review > div.misc-info.clearfix > span.time").text()
if "更新於" in comments_time:
comments_time_array = comments_time.split('更新於')
date_string = comments_time_array[0].replace("\xa0", "").strip()
comments_time_date_time_type = datetime.datetime.strptime(date_string, '%Y-%m-%d')
# 結束時間 2019-01-01
end_time = datetime.datetime.strptime("2018-12-31 23:59", "%Y-%m-%d %H:%M")
# 日期比較 如果到達這個時間。 則不繼續爬
diff = comments_time_date_time_type - end_time
if diff.days <= 0:
# 說明是2018 年 12 月 31 日前的數據
return -1
else:
# 評論時間轉日期類型
comments_time_date_time_type = datetime.datetime.strptime(comments_time, '%Y-%m-%d %H:%M')
# 結束時間 2019-01-01
end_time = datetime.datetime.strptime("2018-12-31 23:59", "%Y-%m-%d %H:%M")
# 日期比較 如果到達這個時間。 則不繼續爬
diff = comments_time_date_time_type - end_time
if diff.days <= 0:
# 說明是2018 年 12 月 31 日前的數據
return -1
# 評論內容 根據上邊的字典,去對應評論區的文字
comments_content = css_decode(dict_css_x_y, dict_svg_text, list_svg_y, comments)
print("評論內容:", comments_content)
worksheet.write(row_index, 0, label=comments_time)
worksheet.write(row_index, 1, label=user_name)
worksheet.write(row_index, 2, label=comments_content)
worksheet.write(row_index, 3, label=len(comments_content))
worksheet.write(row_index, 4, label=pic_link)
worksheet.write(row_index, 5, label="1")
worksheet.write(row_index, 6, label=start_shop)
worksheet.write(row_index, 7, label="1")
worksheet.write(row_index, 8, label="見點贊數中的信息")
worksheet.write(row_index, 9, label=comments_goods_num)
worksheet.write(row_index, 10, label=describe_shop)
row_index += 1
return row_index
def parse_css(ret):
"""
解析css的方法 主要用來處理評論區中的 css 樣式 以及處理加密字體
注意:這里大眾點評有目前有兩種形式,所以我們要針對兩種不同的方式使用不同的解析方式處理:
一種 svg 數據
以 <path id="xxx" d="xx xxx xxx" /> 為鍵
以 <textPath xlink:href="xx" textLength="xx">xxxx</textPath>為value 的形式存儲明文
我們定義為 A 方式
另外一種直接
以 <text x="xxx" y="xxx">xxx</text>的形式存儲
我們定義為 B 方式
:param ret: 網頁源代碼
:return:
dict_svg_text:key - value 形式返回 svg 數據
list_svg_y:
A 方式返回 <path>標簽里的[x,y]坐標軸,以[x,y]形式返回
B 方式返回 <text>標簽里的y數據,x 以自增的形式組裝成[x,y]的形式
dict_css_x_y:css 樣式中,每個加密字體的密文 形如:<svgmtsi class="xxxx"></svgmtsi>之類的
根據class 找到對應樣式中的background: apx bpx 中的[a,b]
is_continue:
是否繼續的標志
"""
# 定義一個是否繼續的標識,True 代表繼續 False 代表不繼續
is_continue = True
# 從當前頁面中找到第一個包含 svgtextcss 關鍵字的 css 路徑 目前來說,頁面一般只有一個
# <link style="text/css" href="//xxxxx/svgtextcss/xxxx.css"/>
css_path_obj = re.search(r'<link re.*?css.*?href=\"(.*/svgtextcss/.*?)\"', ret)
try:
# 組裝完整 url 這個1 代表上述正則中的第二個 (.*?)內容 即 (.*/svgtextcss/.*?)
css_link = "http:" + str(css_path_obj[1])
except:
# 沒找到。為啥沒找到?說明頁面中沒有,為啥沒有。說明頁面抓取失敗- -
is_continue = False
return None, None, None, is_continue
print("獲取 CSS 樣式中...", css_link)
# css 文件的內容 通篇 .className{ background: apx bpx; } 其中有三個帶 url(//) 的
css_html = requests.get(css_link, headers=css_header)
# svg 加密文字的 URL 路徑,目前來說,一個 css 文件中有3 個。經測試,一般第二個是我們使用的,第一個跟第三個我也不知道干啥的,
# 第一個打開是一串數字,大概9 位
# 第三個打開是一小部分明文,但是感覺不太像,跟着感覺走~
svg_link_array = re.compile("\/\/(.*?)\)").findall(str(css_html.text))
# 解析 svg 文本內容
dict_avg_text, list_svg_y = parse_svg_text(svg_link_array)
# 解析 css 樣式表,取background 中 a,b 組裝字典
dict_css_x_y = parse_css_text(css_html.text)
return dict_avg_text, list_svg_y, dict_css_x_y, is_continue
def parse_svg_text(svg_link_array):
"""
解析加密字典的文件
:param svg_link_array: 文件的 URL 數組
:return:
dict_svg:組裝的字典
list_y:
"""
length = 0
svg_html_text = ''
for i in range(0, len(svg_link_array)):
# 請求連接
svg_html = requests.get("http://" + svg_link_array[i], headers=css_header)
content_length = int(svg_html.headers["Content-Length"])
if content_length > length:
length = content_length
svg_html_text = svg_html.text
dict_svg, list_y = parse_svg_test(svg_html_text)
return dict_svg, list_y
def parse_svg_test(svg_html_text):
svg_text_r = r'<textPath xlink:href="(.*?)" textLength="(.*?)">(.*?)</textPath>'
svg_text_re = re.findall(svg_text_r, svg_html_text)
if len(svg_text_re) == 0:
return parse_svg_test_b(svg_html_text)
else:
return parse_svg_test_a(svg_html_text)
# A 方式
def parse_svg_test_a(svg_html):
"""
形如:
http://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/74d63812e5b327d850ab4a8782833d47.svg
頁面上有兩部分內容
一部分是位於上方的 <defs> 下的
<path id="(.*?)" d="(.*?) (.*?) (.*?)"/>
一部分是位於下方的 <text lengthAdjust="spacing"> 下的
<textPath xlink:href="(.*?)" textLength="(.*?)">(.*?)</textPath>
<defs>
<path xmlns="http://www.w3.org/2000/svg" id="32" d="M0 1317 H600"/>
<path xmlns="http://www.w3.org/2000/svg" id="33" d="M0 1364 H600"/>
</defs>
<text lengthAdjust="spacing">
<textPath xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#32" textLength="336">槍畜欄督胖扶遍秒搞笨類敏蛛與診綿病藍份碑往氣焰望</textPath>
<textPath xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#33" textLength="476">腥釘息元易分絕歸當洽疑桂暢朵照仍船從論織朗瓣講首此苗砌能您泛押蜜徐膏</textPath>
<textPath xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#34" textLength="378">數幼段請攻議耐仙鍛征兼句拼愁義食校楚商姻映輛黎鴿怕被測</textPath>
</text>
我們要根據 path 標簽里 d 的第二位,對應 css 樣式表中 background 的bpx 參數
假設有一個 css .ory4hj{background:-168.0px -1353.0px;}
其中 a = -168.0 b = -1353.0
全部取正 1317 < b < 1364 則我們要的字在 1364 的 id 33 中
33 對應 <textPath> 中的 xlink:href="#33"
:param svg_html: 源文件
:return:
"""
svg_text_r = r'<textPath xlink:href="(.*?)" textLength="(.*?)">(.*?)</textPath>'
svg_text_re = re.findall(svg_text_r, svg_html)
dict_avg = {}
i = 0
# 生成svg加密字體庫字典
for data in svg_text_re:
dict_avg[i] = list(data[2])
# print("生成解密字典", i, ":", dict_avg[i])
i += 1
svg_y_r = r'<path id="(.*?)" d="(.*?) (.*?) (.*?)"/>'
svg_y_re = re.findall(svg_y_r, svg_html)
list_y = []
# 存儲('18', 'M0', '748', 'H600') eg:(x坐標,未知,y坐標,未知)
for data in svg_y_re:
data_ = [int(data[0]) - 1, data[2]]
list_y.append(data_)
# print("存儲解密索引", "行號:", data_, "行索引", data[2])
return dict_avg, list_y
# B 方式
def parse_svg_test_b(svg_html):
"""
http://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/ca95f77abfb8871e1fb351905d6f3239.svg
同理。解密思路不變。只是找數據的方式變了。參考方式 A
<text xmlns="http://www.w3.org/2000/svg" x="0" y="805">蓮陡利槽供二本松邊晶嘆帖聰倡夕蝶喇夫拴涌爹激駁醋焦泥屆萄癢送鏈警店柄乒訓能胸露嗓咳般</text>
:param svg_html:
:return:
"""
svg_text_r = r'<text x="(.*?)" y="(.*?)">(.*?)</text>'
svg_text_re = re.findall(svg_text_r, svg_html)
dict_avg = {}
i = 0
# 生成svg加密字體庫字典
for data in svg_text_re:
dict_avg[i] = list(data[2])
i += 1
svg_y_r = r'<text x="(.*?)" y="(.*?)">(.*?)</text>'
svg_y_re = re.findall(svg_y_r, svg_html)
list_y = []
j = 0
for data in svg_y_re:
list_y.append([j, data[1]])
j += 1
return dict_avg, list_y
def parse_css_text(css_html):
"""
http://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/883f229cc13ecde07cc3e3e3af16819e.css
這個就比較簡單了。取.ory4hj{background:-168.0px -1353.0px;} a 值 b 值就行
:param css_html: css 源文件
:return:
"""
css_text_r = r'.(.*?){background:(.*?)px (.*?)px;}'
css_text_re = re.findall(css_text_r, css_html)
dict_css = {}
for data in css_text_re:
"""
加密字庫.ory4hj{background:-168.0px -1353.0px;} 與svg文件對應關系,x/14,根據font-size 計算
y,原樣返回,需要在svg函數中做處理
"""
x = int(float(data[1]) / -14)
"""
字典參數:{css參數名:(background-x,background-y,background-x/14,background-y)}
"""
dict_css[data[0]] = (data[1], data[2], x, data[2])
# print("存儲加密解析數據", "索引值:", data[0], "x 向量: ", data[1], "y 向量:", data[2], "加密字符位置:", x)
return dict_css
def css_decode(css_html, svg_dictionary, svg_list, comments_html):
"""
最終評論匯總
:param css_html: css 的HTML源碼
:param svg_dictionary: svg加密字庫的字典
:param svg_list: svg加密字庫對應的坐標數組[x, y]
:param comments_html: 評論的HTML源碼,對應0-詳情頁的評論,在此處理
:return: 最終合成的評論
"""
css_dict_text = css_html
csv_dict_text, csv_dict_list = svg_dictionary, svg_list
# 處理評論源碼中的 svgmtsi 標簽,生成字典key
comments_text = comments_html.replace('<svgmtsi class="', ',').replace('"/>', ",").replace('">', ",")
comments_list = [x for x in comments_text.split(",") if x != '']
comments_str = []
for msg in comments_list:
# 如果有加密標簽
if msg in css_dict_text:
# 參數說明:[x,y] css樣式中background 的[x/14,y]
x = int(css_dict_text[msg][2])
y = -float(css_dict_text[msg][3])
# 尋找background的y軸比svg<path>標簽里的y軸小的第一個值對應的坐標就是<textPath>的href值
for g in csv_dict_list:
if y < int(g[1]):
comments_str.append(csv_dict_text[int(g[0])][x])
break
# 沒有加密標簽
else:
comments_str.append(msg.replace("\n", "").replace(" ", ""))
str_comments = ""
for x in comments_str:
str_comments += x
# 處理特殊標簽
dr = re.compile(r'</?\w+[^>]*>', re.S)
dr2 = re.compile(r'<img+[^;]*', re.S)
dr3 = re.compile(r'&(.*?);', re.S)
dd = dr.sub('', str_comments)
dd2 = dr2.sub('', dd)
comments_str = dr3.sub('', dd2)
return comments_str
總結:
思路基本上就是分析到抓取的頁面,主要是定位需要的數據在頁面的什么地方,是怎樣的結構
然后模擬請求,正則匹配取需要的數據。py的好處,我個人感覺在於有像 jQuery 一樣的選擇器。方便從頁面上取數據。
最后附上環境相關的
| 項 | 值 |
|---|---|
| 硬件環境 | MacBookPro |
| 軟件環境 | Python 3.8 |
| IDE 版本 | PyCharm 2020.3 |
嗯。別的應該也沒什么啦。就這樣啦。
