【爬虫实例】从B站和某论文网站分析python爬虫的一般编写思路———To someone

2023.12.24修改

问题背景

好久没写爬虫了,前两天友人来问我python能不能爬论文,能不能告诉她爬虫的基本运行原理是什么,跑起来是什么样子。

我一看,论文爬取——爬虫最实用的场景之一,这不拿捏?

在这里插入图片描述

于是便尝试现场演示一番。但是牛皮吹大了,由于手生及记忆有误,代码稀碎运行不出来,一时有些尴尬,趁着今日半天时间重新复习了一遍,查了一些资料,将这个爬虫肝了出来。在此权做记录。

OK不扯皮了,进入正题。

任务分析

需求:
提供一个关键词,先以B站为例,演示一般的爬虫思路,爬取B站热门榜单,随后爬取某论文网站收录的相关论文信息,包括标题,作者,来源,摘要,出版时间,被引用量等等,整理成一个excel表格。

说明:

  • 静态网页的爬取一般都比较简单,一个requests加BeautifulSoup就足矣,一般不涉及翻Network。但现在的网页一般都是动态网页,并且是经过加密处理的,需要对网址进行破译,如果不是使用senlenium这种“可见即可爬”的库,那么找到目标信息所在的真实网址就是必不可少的一环了。

  • 这里先以B站的热门榜信息爬取为引子,先从较简单的网址解析流程开始,随后进入到某论文网站的论文爬取——带参数操作,这块内容涉及动态网页、网址加密、参数翻找及修改等内容。

  • 这两个例子都是需要翻NetWork,仅仅靠解析html是不够的,只会得到[ ]空值

  • 源码都放下面了,运行的时候缺什么模块自己pip就行

B站热门榜单爬取步骤

  1. 思路: 【获取数据】->【解析数据】->【提取数据】->【存储数据】
  • 进入B站热门页面,F12打开开发者工具,点开NetWork,Ctrl+R刷新,勾选Fetch/XHR,出现如下页面:
    在这里插入图片描述
  • 然后依次点开Name下面的文件,一个个看,看哪个文件里藏了想要的标题、up主、播放量等信息。最后定位到发现那个popular?ps=20&pn=1的文件里面藏着我们想要的数据(可以自行试试修改ps和pn参数的值看看有什么效果),【获取数据】完成,如下图:
    在这里插入图片描述
  • 目前可以看到这是一个json文件,所以考虑用json库解析,即把它转成字典后再进行字典操作,一层一层像洋葱一样剥开我们想要的信息。

然后我们点开它的Headers,下图的红色区域就是这个json文件的网址了:
在这里插入图片描述

  • 最后再把提取的数据整理好,放到excel表格里就完成了【储存数据】。
  • 思路映射到代码上用requests库获取这个网址,再用json库把获取到的内容转成字典,再一层一层提取信息,最后写入excel表格存储
  1. 实践:【敲代码】
    先把完整代码放在下面(运行的时候记得改成自己的User-Agent和cookies),随后一行一行分析:
import requests
import pandas as pd

url = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1'  # 信息藏匿的网址,修改ps和pn的值获取更多数据

# 提取cookies
def crawl_cookies():
	"""改成自己的cookies"""
    raw_cookies = "buvid3=C0FDF5DF-5C62-3F5D-3903-11B7ECCFE17679381infoc; b_nut=1678097279; i-wanna-go-back=-1; _uuid=E61EAFB9-679E-7B85-57BD-BB1058B5B7B9980344infoc; header_theme_version=CLOSE; rpdid=|(J~RYuJu)R~0J'uY~)|m~JRR; buvid4=66F96F55-F78D-60C9-3713-9073A85AC0F580950-023030618-YqP%2BXrgApe4TdrhclTJrjQ%3D%3D; buvid_fp_plain=undefined; hit-dyn-v2=1; LIVE_BUVID=AUTO7716781009934090; nostalgia_conf=-1; home_feed_column=5; i-wanna-go-feeds=-1; CURRENT_PID=9f88e740-c8a2-11ed-9805-27604c660a8d; FEED_LIVE_VERSION=V8; b_ut=5; CURRENT_BLACKGAP=0; hit-new-style-dyn=1; CURRENT_FNVAL=4048; SESSDATA=e1253908%2C1709460801%2Cf738b%2A92nQjm8S_WSeB1tqUxuzPWnB5K5uRQa0FP7u4KBBrdmcPeZhcSicbpZB_2lwbjHEIW-ka9aAAAFQA; bili_jct=7013b481e6c2f9d9ae0464e3aeb80336; DedeUserID=629777021; DedeUserID__ckMd5=db15c944cb2dc4dd; enable_web_push=DISABLE; fingerprint=9a10d7db8455d659e5217b58c7ad3b7b; CURRENT_QUALITY=80; buvid_fp=d12b2aa4beee7e6802885ad2e7f2d346; PVID=2; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDA0NTU1NTMsImlhdCI6MTcwMDE5NjI5MywicGx0IjotMX0.Y3O5OCOY8ZYYG6vwZbPdNIz963bVo35Ra7U1-hulZx4; bili_ticket_expires=1700455493; innersign=0; b_lsid=A1952549_18BE1CE3F77; bsource=search_bing; bp_video_offset_629777021=865268213152219208"
    cookies = dict([cookie.split("=", 1) for cookie in raw_cookies.split("; ")])
    # print(cookies)
    return cookies

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0Win64x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48"
    }   # 加个请求头防止被ban
cookies = crawl_cookies()
res = requests.get(url, headers=headers, cookies=cookies)  # 向网址发起请求,获取信息
res_json = res.json()   # 用json把网页信息转成字典
target = res_json['data']['list']   # 用字典方法提取信息
titles = []   # 建一个空列表,后续提取title(标题)
owners = []   # 建一个空列表,后续提取owner(up主)
descs = []    # 建一个空列表,后续提取desc(摘要)
views = []    # 建一个空列表,后续提取view(播放量)

for i in target:
# 把每个视频对应的信息分别提取出来,加到各自的列表中
    titles.append(i['title'])        
    owners.append(i['owner']['name'])
    descs.append(i['desc'])
    views.append(i['stat']['view'])
    print([i['title'], i['owner']['name'], i['desc'], i['stat']['view']])  # 打印看看效果

dic = {"标题": titles, "up主": owners, "简介": descs, "播放量": views}  # 建一个字典
df = pd.DataFrame(dic)    # 字典转pandas.frame类,好写入excel文件
df.to_excel('./Bilibili.xlsx', sheet_name="Sheet1", index=False)  # 信息储存到该文件目录下

代码分析:

  • 前两行导入要用的库;
import requests
import pandas as pd
  • 这一段主要是json部分,因为那个目标信息藏匿的地方的字典结构是这样的↓↓↓
    在这里插入图片描述

  • cookies在这里,直接复制,然后字符串转字典。
    在这里插入图片描述
    按照res_json['data']['list']先提取每个视频的所有信息,再把其中每一个进一步定位提取即可;

    注意:同时记得加上用户个人的cookies!!!不然看到的结果会和实际有差异

url = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1'  # 信息藏匿的网址

# 提取cookies
def crawl_cookies():
    raw_cookies = "buvid3=C0FDF5DF-5C62-3F5D-3903-11B7ECCFE17679381infoc; b_nut=1678097279; i-wanna-go-back=-1; _uuid=E61EAFB9-679E-7B85-57BD-BB1058B5B7B9980344infoc; header_theme_version=CLOSE; rpdid=|(J~RYuJu)R~0J'uY~)|m~JRR; buvid4=66F96F55-F78D-60C9-3713-9073A85AC0F580950-023030618-YqP%2BXrgApe4TdrhclTJrjQ%3D%3D; buvid_fp_plain=undefined; hit-dyn-v2=1; LIVE_BUVID=AUTO7716781009934090; nostalgia_conf=-1; home_feed_column=5; i-wanna-go-feeds=-1; CURRENT_PID=9f88e740-c8a2-11ed-9805-27604c660a8d; FEED_LIVE_VERSION=V8; b_ut=5; CURRENT_BLACKGAP=0; hit-new-style-dyn=1; CURRENT_FNVAL=4048; SESSDATA=e1253908%2C1709460801%2Cf738b%2A92nQjm8S_WSeB1tqUxuzPWnB5K5uRQa0FP7u4KBBrdmcPeZhcSicbpZB_2lwbjHEIW-ka9aAAAFQA; bili_jct=7013b481e6c2f9d9ae0464e3aeb80336; DedeUserID=629777021; DedeUserID__ckMd5=db15c944cb2dc4dd; enable_web_push=DISABLE; fingerprint=9a10d7db8455d659e5217b58c7ad3b7b; CURRENT_QUALITY=80; buvid_fp=d12b2aa4beee7e6802885ad2e7f2d346; PVID=2; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDA0NTU1NTMsImlhdCI6MTcwMDE5NjI5MywicGx0IjotMX0.Y3O5OCOY8ZYYG6vwZbPdNIz963bVo35Ra7U1-hulZx4; bili_ticket_expires=1700455493; innersign=0; b_lsid=A1952549_18BE1CE3F77; bsource=search_bing; bp_video_offset_629777021=865268213152219208"
    cookies = dict([cookie.split("=", 1) for cookie in raw_cookies.split("; ")])
    # print(cookies)
    return cookies

cookies = crawl_cookies()
headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0Win64x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48"}   # 加个请求头防止被ban
res = requests.get(url, headers=headers,cookies=cookies)  # 向网址发起请求,获取信息
res_json = res.json()   # 用json把网页信息转成字典
target = res_json['data']['list']   # 用字典方法提取信息
titles = []   # 建一个空列表,后续提取title(标题)
owners = []   # 建一个空列表,后续提取owner(up主)
descs = []    # 建一个空列表,后续提取desc(摘要)
views = []    # 建一个空列表,后续提取view(播放量)

for i in target:
# 把每个视频对应的信息分别提取出来,加到各自的列表中
    titles.append(i['title'])        
    owners.append(i['owner']['name'])
    descs.append(i['desc'])
    views.append(i['stat']['view'])
    print([i['title'], i['owner']['name'], i['desc'], i['stat']['view']])  # 打印看看效果
  • 下面这段就是把提取到所有数据先转成一个字典,再转成一个dataframe,这样就方便用pandas.to_excel写入了;
dic = {"标题": titles, "up主": owners, "简介": descs, "播放量": views}  # 建一个字典
df = pd.DataFrame(dic)    # 字典转pandas.frame类,好写入excel文件
df.to_excel('./Bilibili_2.xlsx', sheet_name="Sheet1", index=False)  # 信息储存到该文件目录下
  • 最后的结果如下:
    在这里插入图片描述

某论文网站爬取(这网站每过几天就换一个反爬机制,目前这版已经过时了,但是思路没有变化,只需要把GridTable换成新的grid就行,其他参数也是照抄,等我12.25号更新源码)

这个网站的反爬机制比较厉害,查了挺多大佬的文章,想了挺久,写了一个晚上才搞清楚其中原理。

  1. 思路: 【获取数据】->【解析数据】->【提取数据】->【存储数据】
  • 按同样方法进入网站,搜索完关键词(这里我以碳中和为例)后,F12打开开发者工具,点开NetWork,Ctrl+R刷新,勾选Fetch/XHR,出现如下页面:

在这里插入图片描述

  • 一个一个点开看后,发现信息藏在grid文件中,我们点开一看,发现这是一个html语言生成的网页文件,那就先不用json解析了,说不定普通的Beautifulsoup就够用了,以防万一,这次我们做好“伪装”,把完整的请求头(cookies就包含在其中)都带上来访问它。
    在这里插入图片描述

(在响应的Response下面我们也可以看到这个网页文件的源html代码)
在这里插入图片描述

这里我要提个醒,目前这几张图片都是在第一页面翻出来的,写爬虫的时候一定要看翻页后是不是还是一样的参数!!

在这里插入图片描述

这里我们话不多说,开始操作!

  1. 实践:【敲代码】
    先把爬取任意页面论文的完整代码放在下面,改成自己的head和payload,还有页码数pagenum,就可以直接跑,后面一行一行分析:
import requests
from bs4 import BeautifulSoup
import pandas as pd


# 首先还是找到藏着目标信息的network的url
url = 'https://kns.cnki.net/kns8s/brief/grid'

# 注意!这里一定要带全请求头head的所有参数,严格伪装,不然会爬到错误数据,zhiwang这个判定真的很严格
# 所以写爬虫的时候如果没把握或者不偷懒就全加上吧,不要只写个user-agent
head = """
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 1052
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: Ecp_notFirstLogin=V5J6W7; KNS2COOKIE=1703397899.298.33023.402592|b25e41a932fd162af3b8c5cff4059fc3; Ecp_ClientId=n231224140400709904; LID=WEEvREcwSlJHSldSdmVpb1I4NTFzU2U4dlJucHlRbkk0VjRrUDJuSmcwWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!; Ecp_session=1; Ecp_loginuserbk=gz0332; Ecp_LoginStuts={"IsAutoLogin":true,"UserName":"2996950868@qq.com","ShowName":"2996950868%40qq.com","UserType":"jf","BUserName":"gz0332","BShowName":"%E6%B7%B1%E5%9C%B3%E5%A4%A7%E5%AD%A6%E5%9F%8E%E5%9B%BE%E4%B9%A6%E9%A6%86","BUserType":"bk","r":"V5J6W7","Members":[]}; SID_sug=128004; SID_kns_new=kns128002; SID_restapi=128002; personsets=%7B%22classid%22%3A%22YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CEMRPGLPA%2CWQ0UVIAA%2CBLZOG7CK%2CPWFIRAGL%2CNN3FJMUV%2CNLBO1Z6R%22%2C%22maxgroup%22%3A%2220%22%2C%22autofold%22%3A%22true%22%2C%22time%22%3A%221645180182793%22%2C%22order%22%3A%22DFR%22%7D; dblang=both; Ecp_loginuserjf=gz0332; c_m_LinID=LinID=WEEvREcwSlJHSldSdmVpb1I4NTFzU2U4dlJucHlRbkk0VjRrUDJuSmcwWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!&ot=12%2F24%2F2023%2014%3A25%3A06; c_m_expire=2023-12-24%2014%3A25%3A06; Ecp_ClientIp=219.223.232.138
Host: kns.cnki.net
Origin: https://kns.cnki.net
Referer: https://kns.cnki.net/kns8s/defaultresult/index?crossids=YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CWQ0UVIAA%2CBLZOG7CK%2CEMRPGLPA%2CPWFIRAGL%2CNLBO1Z6R%2CNN3FJMUV&korder=SU&kw=%E7%A2%B3%E4%B8%AD%E5%92%8C
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/120.0.0.0 Safari/537.36
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
"""

# 将headers打包成字典
headers = dict([[y.strip() for y in x.strip().split(':', 1)] for x in head.strip().split('\n') if x.strip()])

# payload的boolsearch参数直接设置为false,因为从第二页开始所有的boolsearch就都是false了
# 这里只需要改pageNum就能爬到目标页面的论文信息
num = '2'  # 这里是指爬取第二页的文章,为str格式
payload = """
boolSearch: false
QueryJson: {"Platform":"","Resource":"CROSSDB","Classid":"WD0FTY92","Products":"","QNode":{"QGroup":[{"Key":"Subject","Title":"","Logic":0,"Items":[{"Field":"SU","Value":"碳中和","Operator":"TOPRANK","Logic":0}],"ChildItems":[]}]},"ExScope":1,"SearchType":7,"Rlang":"CHINESE","KuaKuCode":"YSTT4HG0,LSTPFY1C,JUP3MUPD,MPMFIG1A,EMRPGLPA,WQ0UVIAA,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R"}
pageNum: $num 
pageSize: 20
sortField: DFR
sortType: desc
dstyle: listmode
boolSortSearch: false
productStr: YSTT4HG0,LSTPFY1C,RMJLXHZ3,JQIRZIYA,JUP3MUPD,1UR4K4HZ,BPBAFJ5S,R79MZMCB,MPMFIG1A,EMRPGLPA,J708GVCE,ML4DRIDX,WQ0UVIAA,NB3BWEHK,XVLO76FD,HR1YT1Z9,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R,
aside: 
searchFrom: 资源范围:总库
"""
payload = payload.replace('$num', num) # 替换页码

# 将payload打包成字典
data = dict([[y.strip() for y in x.strip().split(':', 1)] for x in payload.strip().split('\n') if x.strip()])  

# 发送请求并接收网页信息
res = requests.post(url, headers=headers, data=data).content.decode('utf-8')
bs = BeautifulSoup(res, 'html.parser')

# 按索引找到目标信息
target = bs.find('table', class_="result-table-list")
names = target.find_all(class_='name')
authors = target.find_all(class_="author")
sources = target.find_all(class_="source")
dates = target.find_all(class_="date")
datas = target.find_all(class_="data")
database = []

# 将所有信息进行整理
for k in range(len(names)):
    name = names[k].find('a').text.strip()
    author = authors[k].text.strip()
    source = sources[k].text.strip()
    date = dates[k].text.strip()
    data = datas[k].text.strip()
    href = names[k].find('a')['href']  # 提取文章链接
    database.append([name, author, source, date, data, href])

# 写入excel文件 
df = pd.DataFrame(database, columns=["Title", "Author", "Source", "Publish_date", "Database", "Link"])
df.to_excel('paper.xlsx', sheet_name="Sheet1", index=False)
print("done")

代码分析:

  • 前三行导入相关库,这里经过尝试,发现requests库和BeautifulSoup两个最常用的就足够解析网页了,当然也可以用lxmt.html来解析网页。

  • 然后把找到的目标网址赋好值为url(和前面一样,在Headers里找)。

import requests
from bs4 import BeautifulSoup
import pandas as pd

# 首先还是找到藏着目标信息的network的url
url = 'https://kns.cnki.net/kns8s/brief/grid'

  • 这一段很容易,就是把Headers里的请求头copy过来赋给新的变量,然后转成字典。
# 注意!这里一定要带全请求头head的所有参数,严格伪装,不然会爬到错误数据,zhiwang这个判定真的很严格
# 所以写爬虫的时候如果没把握或者不偷懒就全加上吧,不要只写个user-agent
head = """
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 1052
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: Ecp_notFirstLogin=V5J6W7; KNS2COOKIE=1703397899.298.33023.402592|b25e41a932fd162af3b8c5cff4059fc3; Ecp_ClientId=n231224140400709904; LID=WEEvREcwSlJHSldSdmVpb1I4NTFzU2U4dlJucHlRbkk0VjRrUDJuSmcwWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!; Ecp_session=1; Ecp_loginuserbk=gz0332; Ecp_LoginStuts={"IsAutoLogin":true,"UserName":"2996950868@qq.com","ShowName":"2996950868%40qq.com","UserType":"jf","BUserName":"gz0332","BShowName":"%E6%B7%B1%E5%9C%B3%E5%A4%A7%E5%AD%A6%E5%9F%8E%E5%9B%BE%E4%B9%A6%E9%A6%86","BUserType":"bk","r":"V5J6W7","Members":[]}; SID_sug=128004; SID_kns_new=kns128002; SID_restapi=128002; personsets=%7B%22classid%22%3A%22YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CEMRPGLPA%2CWQ0UVIAA%2CBLZOG7CK%2CPWFIRAGL%2CNN3FJMUV%2CNLBO1Z6R%22%2C%22maxgroup%22%3A%2220%22%2C%22autofold%22%3A%22true%22%2C%22time%22%3A%221645180182793%22%2C%22order%22%3A%22DFR%22%7D; dblang=both; Ecp_loginuserjf=gz0332; c_m_LinID=LinID=WEEvREcwSlJHSldSdmVpb1I4NTFzU2U4dlJucHlRbkk0VjRrUDJuSmcwWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!&ot=12%2F24%2F2023%2014%3A25%3A06; c_m_expire=2023-12-24%2014%3A25%3A06; Ecp_ClientIp=219.223.232.138
Host: kns.cnki.net
Origin: https://kns.cnki.net
Referer: https://kns.cnki.net/kns8s/defaultresult/index?crossids=YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CWQ0UVIAA%2CBLZOG7CK%2CEMRPGLPA%2CPWFIRAGL%2CNLBO1Z6R%2CNN3FJMUV&korder=SU&kw=%E7%A2%B3%E4%B8%AD%E5%92%8C
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/120.0.0.0 Safari/537.36
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
"""
headers = dict([[y.strip() for y in x.strip().split(':', 1)] for x in head.strip().split('\n') if x.strip()])

在这里找↓↓↓
在这里插入图片描述

  • 这一段把payload参数复制下来,方法和上面是一样的,直接copy+赋值+转字典;
    *这里注意我设置的num是2,即爬取第二页而不是第一页*
# payload的boolsearch参数直接设置为false,因为从第二页开始所有的boolsearch就都是false了
# 这里只需要改pageNum就能爬到目标页面的论文信息
num = '2'  # 这里是指爬取第二页的文章,为str格式
payload = """
boolSearch: false
QueryJson: {"Platform":"","Resource":"CROSSDB","Classid":"WD0FTY92","Products":"","QNode":{"QGroup":[{"Key":"Subject","Title":"","Logic":0,"Items":[{"Field":"SU","Value":"碳中和","Operator":"TOPRANK","Logic":0}],"ChildItems":[]}]},"ExScope":1,"SearchType":7,"Rlang":"CHINESE","KuaKuCode":"YSTT4HG0,LSTPFY1C,JUP3MUPD,MPMFIG1A,EMRPGLPA,WQ0UVIAA,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R"}
pageNum: $num 
pageSize: 20
sortField: DFR
sortType: desc
dstyle: listmode
boolSortSearch: false
productStr: YSTT4HG0,LSTPFY1C,RMJLXHZ3,JQIRZIYA,JUP3MUPD,1UR4K4HZ,BPBAFJ5S,R79MZMCB,MPMFIG1A,EMRPGLPA,J708GVCE,ML4DRIDX,WQ0UVIAA,NB3BWEHK,XVLO76FD,HR1YT1Z9,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R,
aside: 
searchFrom: 资源范围:总库
"""
payload = payload.replace('$num', num) # 替换页码

# 将payload打包chengzidian
data = dict([[y.strip() for y in x.strip().split(':', 1)] for x in payload.strip().split('\n') if x.strip()])  

现在我们先翻页,进到第二页的论文,比较一下两个grid的payload参数,会发现boolsearch的值一个是true一个是false,其他的参数也有一点点差异。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
经过测试,发现从第二页开始,其后的所有页码的boolsearch都是false,而且即使pageNum为1时,boolsearchfalse依然可以成功爬取,所以这里我们直接把第二页grid的payload参数复制下来即可。
在这里插入图片描述

  • 接下来定位信息,代码的逻辑和前面爬B站用json是一样的,只不过这里是用Beautifulsoup来解析网页,用 findfind_all方法来定位元素。
# 发送请求并接收网页信息
res = requests.post(url, headers=headers, data=data).content.decode('utf-8')
bs = BeautifulSoup(res, 'html.parser')

# 按索引找到目标信息
target = bs.find('table', class_="result-table-list")
names = target.find_all(class_='name')
authors = target.find_all(class_="author")
sources = target.find_all(class_="source")
dates = target.find_all(class_="date")
datas = target.find_all(class_="data")
database = []

# 将所有信息进行整理
for k in range(len(names)):
    name = names[k].find('a').text.strip()
    author = authors[k].text.strip()
    source = sources[k].text.strip()
    date = dates[k].text.strip()
    data = datas[k].text.strip()
    href = names[k].find('a')['href']  # 提取文章链接
    database.append([name, author, source, date, data, href])
  • 最后一个部分就是保存论文数据啦~记得储存到excel文件中(^ ▽ ^)
# 写入excel文件 
df = pd.DataFrame(database, columns=["Title", "Author", "Source", "Publish_date", "Database", "Link"])
df.to_excel('paper_2.xlsx', sheet_name="Sheet1", index=False)
print("done")
  • 结果如下↓↓↓
    在这里插入图片描述

问题及回顾

  • 问题:

虽然最后的爬虫的确有相对理想的效果,但是在写代码过程遇到以下三个问题还未想明白,希望有大佬能在评论区留下解答:

① B站爬取的数据和实时页面有一定出入 ,比如我爬取的第一个数据标题是【这也太不合理了吧】,但是按照那个网页解析出来的第一个应该是【AI一眼就看透了我的本质】,我目前有两个怀疑方向:一是这个热门榜是不停在变的,每隔一段时间就会变化;二是我翻到的那个网址并不是目标网址。但这两种怀疑在网页解析过程中都没找到痕迹,不知道具体是什么原因。

>>>2023.11.18 问题①已解决,且已修改上文代码,原因在于未带cookies,此处涉及到推荐算法,B站其实是有个性化推送机制的,每个人看到的热门榜单其实是不同的,由用户偏好决定,而要追踪到这个榜单就要用到用户的cookies。如果不加cookies参数(和我之前做的一样),会出现一个和你在B站看到的榜单大概率不同的数据,应该是新用户的默认热门榜单。

② 在爬论文时,我其实一开始没有专门def一个函数出来,完全是因为这段代码会有以下报错
Traceback (most recent call last): File "D:\PyTest\爬虫\COVID-19\spider.py", line 91, in <module> data['CurPage'] = str(j) TypeError: 'str' object does not support item assignment
希望这里能有大哥解答这里为什么会涉及字符无法更改的报错。

## 这段代码就是在  data = dict([[y.strip() for y in x.strip().split(':', 1)] for x in head.strip().split('\n') if x.strip()]) 那一行的后面
for j in range(2, 5):    
    data['CurPage'] = str(j)
    res = requests.post(url, timeout=30, data=data,
                        headers=headers).content.decode('utf-8')
    bs = BeautifulSoup(res, 'html.parser')
    target = bs.find('table', class_="result-table-list")
    names = target.find_all(class_='name')
    authors = target.find_all(class_="author")
    sources = target.find_all(class_="source")
    dates = target.find_all(class_="date")
    datas = target.find_all(class_="data")

    for k in range(len(names)):
        name = names[k].find('a').text.strip()
        author = authors[k].text.strip()
        source = sources[k].text.strip()
        date = dates[k].text.strip()
        data = datas[k].text.strip()
        href = "https://kns.cnki.net/" + names[k].find('a')['href']  # 提取文章链接

③ 论文页第一page将一个重要参数sqlsearch隐藏,当时没有往后翻,导致浪费大量时间,这种操作是如何实现的?不是很能凭直觉猜测和理解。

  • 回顾:

本文尝试将爬虫的四个步骤映射到实际需求中进行操作,但技术水平有限,权做一次爬虫基本知识的复习梳理以及同友人的交流机会haha~

可以看到,网页解析方式是多种多样的,网站加密方式也是在不断进步的。

今天课上我玩了一下ChatGPT,要他写一个B站热门的爬取代码,因为GPT数据库的问题,他写出来的代码确实能解决20年左右的B站爬取问题,但如今因为网站加密方式改进,代码已经运行不出效果了,还有其他许多网站也是如此。

不过,仅针对文本资源爬取的话,本文涉及的思路和步骤应该是绝大部分爬虫工作时都绕不开的。

最后,感谢每位经验分享者,在下只是一只小小爬虫,唯有在大家的知识网站中游弋才能爬取智慧。

在这里插入图片描述
(顺带吐槽一嘴,CSDN的这个图片水印真丑捏╮(╯▽╰)╭)

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值