聲明:全過程沒有任何違法操作
概要
目標:爬取佰騰網上的專利信息
目標網址:https://www.baiten.cn/
過程
首先我們打開佰騰網(推薦使用谷歌瀏覽器,別問我為什么),頁面如下圖所示
很明顯這個網站需要登陸,但是觀察這個網站頁面,是沒有專利展示的,所以我們可以先搜索一類,這里我用java示例。
打開這個頁面我們會發現依然沒有專利展示出來(為了方便操作,我自己開了個賬號登錄網頁),所以我們需要這個網頁的Cookie,它能幫我們減少登錄操作
登錄后,我們按下F12,找到這個網頁請求表頭的cookie(注意,這個cookie過一段時間后會改變,所以需要即拿即用)
然后我們創建一個示例1.py文件,用來獲取這個網頁的頁面內容
# 示例1.py代碼
import requests
url = 'https://www.baiten.cn/results/l/java/.html?type=l'
headers = {
'Content-Type':'application/x-www-form-urlencoded',
'charset':'UTF-8',
'Host':'www.baiten.cn',
'Referer':'https://www.baiten.cn/results/l/java/.html?type=l',
'Cookie':'UM_distinctid=17cee04232917-0ce7616d753d2c-57b1a33-144000-17cee04232a152; BSESSION=ebd591955fd30d1257027dbb9963df56f77fa57e2713e5a3; CNZZDATA1275904268=820333148-1636079972-|1637373846; JSESSIONID=2CEB334BD9F1C7AB209ABCC0E1FCD9B6; Hm_lvt_7fc44f078bf7b5e19489428c362109a3=1637371390,1637376101,1637380097,1637380316; PD=ef38206be5b44e431a74453174c7df9bf2477939ae99f267254b09e8136946e9bd45a801cd1d3486; Hm_lpvt_7fc44f078bf7b5e19489428c362109a3=1637380570; yunsuo_session_verify=58a343a92e57e07624bb552dd150650c',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
response = requests.get(url=url, headers=headers).text
print(response)
通過運行代碼我們獲取到了這個網頁的頁面內容(如下)
看着這個頁面的內容是不是很復雜,所以我們下意識就想要找到表示更簡潔的數據內容,這時我們需要了解json格式的文件
JSON(JavaScriptObjectNotation)是一種輕量級的數據交換格式。易於人閱讀和編寫。同時也易於機器解析和生成。它基於JavaScriptProgrammingLanguage,StandardECMA-2623rdEdition-December1999的一個子集。
如果說更深入的了解的話,json格式的內容相當於python中的一個字典類型,因此json格式的數據通常是多個鍵與鍵值綁定在一起的數據。例如:
jsondata = {'name' = 'xxx','age' = 'xxx','address' = 'xxx'}
其中{'name' = 'xxx','age' = 'xxx','address' = 'xxx'}就是json文件中的數據了
一般情況,我們需要在Fetch/XHR欄中找到相應的xhr類型的網址,因為這個網址中包含了這個頁面中數據的內容(但注意,一些網站可能會將這些網址設置‘防爬’,比如我將要爬取數據的網站)
我們先點開Fetch/XHR欄,發現一串亂碼的網址(如下,注意:經測試,這個亂碼會變化)
說不定這個網址里就有我們需要的數據,打開一看,頁面內容顯示405,並且提示我們這個網址不能用get方式申請。但是我們點開前面的載荷,會發現里面有表單數據,因此這網址里絕逼有我們需要的數據,只是我們需要用post方式申請。
因此,我們創建一個示例2.py文件,用於post申請(如下)
# 示例2.py文件代碼(注意,代碼中的data是該網址的表單數據,必須寫完整)
import requests
url = 'https://www.baiten.cn/results/list/NGNkYWE3MDQ1YzM4MjBjZjliMDA5ZGU1ZTlhNGJlZWM2YTI5YWYyNTVmOTU3ZWEyOTE4OGMzNjM4YjczMmU3MWJmZTRmOTc0N2NiOGEyNmM='
data = {
'sc':'',
'q':'java',
'sort':'',
'sortField':'',
'fq':'',
'pageSize':20,
'pageIndex':1,
'type':'l',
'merge':'no-merge'
}
headers = {
'Content-Length':'79',
'Content-Type':'application/x-www-form-urlencoded',
'charset':'UTF-8',
'Host':'www.baiten.cn',
'Origin':'https://www.baiten.cn',
'Referer':'https://www.baiten.cn/results/l/java/.html?type=l',
'Cookie':'UM_distinctid=17cee04232917-0ce7616d753d2c-57b1a33-144000-17cee04232a152; BSESSION=ebd591955fd30d1257027dbb9963df56f77fa57e2713e5a3; CNZZDATA1275904268=820333148-1636079972-|1637373846; JSESSIONID=2CEB334BD9F1C7AB209ABCC0E1FCD9B6; Hm_lvt_7fc44f078bf7b5e19489428c362109a3=1637371390,1637376101,1637380097,1637380316; PD=ef38206be5b44e431a74453174c7df9bf2477939ae99f267254b09e8136946e9bd45a801cd1d3486; yunsuo_session_verify=58a343a92e57e07624bb552dd150650c; Hm_lpvt_7fc44f078bf7b5e19489428c362109a3=1637380857',
'sec-ch-ua-mobile':'?0',
'sec-ch-ua-platform':'Windows',
'Sec-Fetch-Dest':'empty',
'Sec-Fetch-Mode':'cors',
'Sec-Fetch-Site':'same-origin',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
requests1 = requests.post(url, headers=headers, data=data).json()
結果代碼報錯,說明這里返回的數據可能不是json數據,我們在將json()改為text來獲取頁面內容
得到了PT_Offline_Login的內容,說明我們使用的cookie可能過期了,我再次更換了新的cookie后,得到的結果是“非法操作”
當我再一次刷新頁面時,我發現這個申請頭地址改變了,所以貓膩一定就在這個申請頭后面的亂碼上了。
果然,當我在這個網站上尋找相關的信息時,我找到一個網址,里面正好有與非法操作相關的內容
觀察這一段代碼,發現應該是網站使用了encode64函數加密了window.token變量,說不定這個變量加密的結果就是那一串亂碼,所以我們還需要找到加密方法encode64,果然,我在另一個網站中找到了加密方法
我們在python中運行一下這段代碼(需要安裝execjs模塊來實現)
創建示例3.py文件實現加密方法
# 示例3.py代碼
import execjs
ctx1 = execjs.compile("""
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function encode64(input) {
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64
} else {
if (isNaN(chr3)) {
enc4 = 64
}
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = ""
} while (i < input.length);
return output;
}
""")
print(ctx1.call("encode64", 'hasbdkjg8923876ajsbdahsd7a68ds7sa786d7aufkasd'))
加密功能實現
然后我們只需要找到window.token這個變量就ok了。回到示例1,運行代碼得到請求網頁的內容,然后在內容中果然能發現這個變量,而且這個變量顯示的位置不變,且每次一刷新,這個變量每次都會改變
然后我們提取這個window.token,再填寫到加密方法中,發現加密過后的內容果然是那串亂碼。到這里位置,我們就可以實現爬取佰騰網專利數據了
實現代碼(注意cookie隔一段時間會失效)
import requests
import execjs
pageSize = int(input('pageSize(不要大於100):'))
pageIndex = int(input('pageIndex(盡量小點):'))
# 獲取到申請頭網址的html頁面數據
url = 'https://www.baiten.cn/results/l/java/.html?type=l'
headers = {
'Content-Type':'application/x-www-form-urlencoded',
'charset':'UTF-8',
'Host':'www.baiten.cn',
'Referer':'https://www.baiten.cn/results/l/java/.html?type=l',
'Cookie':'UM_distinctid=17cee04232917-0ce7616d753d2c-57b1a33-144000-17cee04232a152; BSESSION=ebd591955fd30d1257027dbb9963df56f77fa57e2713e5a3; CNZZDATA1275904268=820333148-1636079972-|1637373846; JSESSIONID=DB3783395EF3493A6442823BB3B78D64; Hm_lvt_7fc44f078bf7b5e19489428c362109a3=1637240043,1637284980,1637371390,1637376101; PD=e93016ff6f0fe59581642611bf9938c051020e206df9821a71254bbb888e02a4b29a183d16a1bf5e; Hm_lpvt_7fc44f078bf7b5e19489428c362109a3=1637376109; yunsuo_session_verify=ef4e7c0696a7484f649a0004398e3fdb',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
response = requests.get(url=url, headers=headers).text
str = response
# 獲取到被加密的數據token
token = ''
for i in range(5568, 5648):
token = token + str[i]
# 利用execjs模塊對獲取到的token進行加密
ctx1 = execjs.compile("""
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function encode64(input) {
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64
} else {
if (isNaN(chr3)) {
enc4 = 64
}
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = ""
} while (i < input.length);
return output;
}
""")
ctx1.call("encode64", token)
# 獲取加密網站https://www.baiten.cn/results/list/token
url1 = ''
str1 = 'https://www.baiten.cn/results/list/'
str2 = ctx1.call("encode64", token)
url1 = url1 + str1 + str2
# 爬取到被加密的網站上專利的數據
url2 = url1
data = {
'sc':'',
'q':'java',
'sort':'',
'sortField':'',
'fq':'',
'pageSize':pageSize,
'pageIndex':pageIndex,
'type':'l',
'merge':'no-merge'
}
headers = {
'Content-Length':'79',
'Content-Type':'application/x-www-form-urlencoded',
'charset':'UTF-8',
'Host':'www.baiten.cn',
'Origin':'https://www.baiten.cn',
'Referer':'https://www.baiten.cn/results/l/java/.html?type=l',
'Cookie':'UM_distinctid=17cee04232917-0ce7616d753d2c-57b1a33-144000-17cee04232a152; BSESSION=ebd591955fd30d1257027dbb9963df56f77fa57e2713e5a3; CNZZDATA1275904268=820333148-1636079972-|1637373846; JSESSIONID=DB3783395EF3493A6442823BB3B78D64; Hm_lvt_7fc44f078bf7b5e19489428c362109a3=1637240043,1637284980,1637371390,1637376101; PD=e93016ff6f0fe59581642611bf9938c051020e206df9821a71254bbb888e02a4b29a183d16a1bf5e; Hm_lpvt_7fc44f078bf7b5e19489428c362109a3=1637376109; yunsuo_session_verify=ef4e7c0696a7484f649a0004398e3fdb',
'sec-ch-ua-mobile':'?0',
'sec-ch-ua-platform':'Windows',
'Sec-Fetch-Dest':'empty',
'Sec-Fetch-Mode':'cors',
'Sec-Fetch-Site':'same-origin',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
requests1 = requests.post(url2, headers=headers, data=data).json()
result = requests1['cubePatentSearchResponse']['documents']
patentdata = open('data.txt', 'w+')
patentdata.write('第')
patentdata.write('%d' %pageIndex)
patentdata.write('頁的專利產品' + '\n')
i = 0
while i < pageSize:
result = requests1['cubePatentSearchResponse']['documents'][i]['field_values']
ti = result['ti'] # 專利名稱
id = result['id'] # 申請號
pn = result['pn'] # 公開號
pa = result['pa'] # 申請(專利權)人是個列表
str_pa = ''
for m1 in pa:
str_pa = str_pa + ' ' + m1
In = result['in'] # 發明人是個列表
str_in = ''
for m2 in pa:
str_in = str_in + ' ' + m2
ad = result['ad'] # 申請日
pd = result['pd'] # 公開日
ic1 = result['ic1'] # 主分類號
aic1 = result['aic1'] # 分類號
aa = result['aa'] # 地址
co = result['co'] # 國省代碼
kw = result['kw'] # 技術關鍵詞
str_kw = ''
for m3 in kw:
str_kw = str_kw + ' ' + m3
ls1 = result['ls1'] # 專利授權情況
ab = result['ab'] # 摘要
ac = result['ac'] # 學術搜索
num = i+1
patentdata.write('第')
patentdata.write('%d' %num)
patentdata.write('個專利產品' + '\n')
patentdata.write('專利名稱:' + ti + '\n')
patentdata.write('申請號:' + id + '\n')
patentdata.write('公開號:' + pn + '\n')
patentdata.write('申請(專利權)人:' + str_pa + '\n')
patentdata.write('發明人:' + str_in + '\n')
patentdata.write('申請日:' + ad + '\n')
patentdata.write('公開日:' + pd + '\n')
patentdata.write('主分類號:' + ic1 + '\n')
patentdata.write('分類號:' + aic1 + '\n')
patentdata.write('地址:' + aa + '\n')
patentdata.write('國省代碼:' + co + '\n')
patentdata.write('技術關鍵詞:' + str_kw + '\n')
patentdata.write('專利授權情況:' + ls1 + '\n')
patentdata.write('摘要:' + ab + '\n')
patentdata.write('學術搜索:' + ac + '\n')
i += 1
else:
print("數據獲取完畢!")
爬取效果(這里我們以第3頁每頁100個專利的設定爬取有關java的專利)
當然,我們也可以爬取其他類的專利,只需要修改相關代碼和cookie就可以實現了,但是最最最重要的一點就是cookie是會失效的。
這里附上碼雲鏈接
感受
為了實現這個目標,我除了自學爬蟲外,關於網頁的很多知識都向我哥請教過,我也因此知道了很多比如html格式,json格式以及異步加載等等。給我最大的感受,那還是學習我們這塊專業的人,就應當有終生學習的理念。
更新(添加注入SQL數據庫和更新數據的功能)
代碼實現如下
# 注入數據
def savadatatosql():
con = sqlite3.connect('patents.db')
cursor = con.cursor()
i = 0
while i < pageSize:
result = requests1['cubePatentSearchResponse']['documents'][i]['field_values']
i_ti = result['ti'] # 專利名稱
i_id = result['id'] # 申請號
i_pn = result['pn'] # 公開號
i_pa = result['pa'] # 申請(專利權)人是個列表
i_str_pa = ''
for m1 in i_pa:
i_str_pa = i_str_pa + ' ' + m1
i_in = result['in'] # 發明人是個列表
i_str_in = ''
for m2 in i_in:
i_str_in = i_str_in + ' ' + m2
i_ad = result['ad'] # 申請日
i_pd = result['pd'] # 公開日
i_ic1 = result['ic1'] # 主分類號
i_aic1 = result['aic1'] # 分類號
i_aa = result['aa'] # 地址
i_co = result['co'] # 國省代碼
i_kw = result['kw'] # 技術關鍵詞
i_str_kw = ''
for m3 in i_kw:
i_str_kw = i_str_kw + ' ' + m3
i_ls1 = result['ls1'] # 專利授權情況
i_ab = result['ab'] # 摘要
i_ac = result['ac'] # 學術搜索
sql = 'INSERT INTO classjava (專利名稱, 申請號, 公開號, 申請(專利權)人, 發明人, 申請日, 公開日, 主分類號, 分類號, 地址, 國省代碼, 技術關鍵詞, 專利授權情況, 摘要, 學術搜索) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
cursor.execute(sql, [i_ti, i_id, i_pn, i_str_pa, i_str_in, i_ad, i_pd, i_ic1, i_aic1, i_aa, i_co, i_str_kw, i_ls1, i_ab, i_ac])
con.commit()
print("第{}條數據獲取成功。".format(i+1))
i += 1
time.sleep(0.5)
else:
cursor.close()
con.close()
print("數據獲取完畢!")
# 更新數據
def dataupdate():
con = sqlite3.connect('patents.db')
cursor = con.cursor()
i = 0
while i < pageSize:
result = requests1['cubePatentSearchResponse']['documents'][i]['field_values']
i_ti = result['ti'] # 專利名稱
i_id = result['id'] # 申請號
i_pn = result['pn'] # 公開號
i_pa = result['pa'] # 申請(專利權)人是個列表
i_str_pa = ''
for m1 in i_pa:
i_str_pa = i_str_pa + ' ' + m1
i_in = result['in'] # 發明人是個列表
i_str_in = ''
for m2 in i_in:
i_str_in = i_str_in + ' ' + m2
i_ad = result['ad'] # 申請日
i_pd = result['pd'] # 公開日
i_ic1 = result['ic1'] # 主分類號
i_aic1 = result['aic1'] # 分類號
i_aa = result['aa'] # 地址
i_co = result['co'] # 國省代碼
i_kw = result['kw'] # 技術關鍵詞
i_str_kw = ''
for m3 in i_kw:
i_str_kw = i_str_kw + ' ' + m3
i_ls1 = result['ls1'] # 專利授權情況
i_ab = result['ab'] # 摘要
i_ac = result['ac'] # 學術搜索
sql = 'UPDATE classjava SET 專利名稱=?, 申請號=?, 公開號=?, 申請(專利權)人=?, 發明人=?, 申請日=?, 公開日=?, 主分類號=?, 分類號=?, 地址=?, 國省代碼=?, 技術關鍵詞=?, 專利授權情況=?, 摘要=?, 學術搜索=? WHERE 序號=?'
cursor.execute(sql, [i_ti, i_id, i_pn, i_str_pa, i_str_in, i_ad, i_pd, i_ic1, i_aic1, i_aa, i_co, i_str_kw, i_ls1, i_ab, i_ac, i+1])
con.commit()
print("第{}條數據更新成功。".format(i+1))
i += 1
time.sleep(0.5)
else:
cursor.close()
con.close()
print("數據更新完畢!")
print("操作:" + '\n' + '1.獲取數據(如果已經獲取了數據,請不要使用此操作)' + '\n' + '2.更新數據')
operation = int(input('請輸入你需要執行的操作的數字代號:'))
if operation == 1:
savadatatosql()
elif operation == 2:
dataupdate()
else:
print('操作無效,程序終止')
代碼實現效果
更新前
開始更新
更新完畢(解釋一下這里為什么數據是一樣的,因為我沒有關閉數據庫導致數據無法更新,需要重新關閉后再打開才能更新數據,這里是我本人的一個小失誤)
當然,在我設計的時候我發現上面的代碼不夠簡潔,所以我將代碼改得更加簡潔了一點,如下
def datatosql():
con = sqlite3.connect('patents.db')
cursor = con.cursor()
i = 0
print("操作:" + '\n' + '1.獲取數據(如果已經獲取了數據,請不要使用此操作)' + '\n' + '2.更新數據')
operation = int(input('請輸入你需要執行的操作的數字代號:'))
while i < pageSize:
result = requests1['cubePatentSearchResponse']['documents'][i]['field_values']
i_ti = result['ti'] # 專利名稱
i_id = result['id'] # 申請號
i_pn = result['pn'] # 公開號
i_pa = result['pa'] # 申請(專利權)人是個列表
i_str_pa = ''
for m1 in i_pa:
i_str_pa = i_str_pa + ' ' + m1
i_in = result['in'] # 發明人是個列表
i_str_in = ''
for m2 in i_in:
i_str_in = i_str_in + ' ' + m2
i_ad = result['ad'] # 申請日
i_pd = result['pd'] # 公開日
i_ic1 = result['ic1'] # 主分類號
i_aic1 = result['aic1'] # 分類號
i_aa = result['aa'] # 地址
i_co = result['co'] # 國省代碼
i_kw = result['kw'] # 技術關鍵詞
i_str_kw = ''
for m3 in i_kw:
i_str_kw = i_str_kw + ' ' + m3
i_ls1 = result['ls1'] # 專利授權情況
i_ab = result['ab'] # 摘要
i_ac = result['ac'] # 學術搜索
# 注入數據
if operation == 1:
sql = 'INSERT INTO classjava (專利名稱, 申請號, 公開號, 申請(專利權)人, 發明人, 申請日, 公開日, 主分類號, 分類號, 地址, 國省代碼, 技術關鍵詞, 專利授權情況, 摘要, 學術搜索) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
cursor.execute(sql, [i_ti, i_id, i_pn, i_str_pa, i_str_in, i_ad, i_pd, i_ic1, i_aic1, i_aa, i_co, i_str_kw,
i_ls1, i_ab, i_ac])
con.commit()
print("第{}條數據獲取成功。".format(i + 1))
i += 1
time.sleep(0.5)
elif operation == 2:
sql = 'UPDATE classjava SET 專利名稱=?, 申請號=?, 公開號=?, 申請(專利權)人=?, 發明人=?, 申請日=?, 公開日=?, 主分類號=?, 分類號=?, 地址=?, 國省代碼=?, 技術關鍵詞=?, 專利授權情況=?, 摘要=?, 學術搜索=? WHERE 序號=?'
cursor.execute(sql, [i_ti, i_id, i_pn, i_str_pa, i_str_in, i_ad, i_pd, i_ic1, i_aic1, i_aa, i_co, i_str_kw,
i_ls1, i_ab, i_ac, i + 1])
con.commit()
print("第{}條數據更新成功。".format(i + 1))
i += 1
time.sleep(0.5)
else:
print('操作無效,程序終止')
break
if operation == 1:
cursor.close()
con.close()
print("數據獲取完畢!")
elif operation == 2:
cursor.close()
con.close()
print("數據更新完畢!")
datatosql()
運行效果如下
這里也是剛才沒有關閉數據庫的情況,再次打開就是下面圖的效果