Python 爬虫小练习:基于 XPath 的表格信息爬取

这是一个 Python 爬虫的入门练习,我们通过 Request 请求数据,并通过 XPath 去匹配表格中的元素或者内容,使用 Pandas 进行数据的整理。下面我们 Step by Step 来完成这件事情。

确定目标和分析思路

目标

目标是从某个网站中爬取表格数据。我选择的网站是:http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList (下不妨称为名单页)。

  • 这里有一个名单库,名单库里面有一些个人信息,包括姓名、单位名称等等。
  • 名单分页,需要不断点下一页加载更多信息。
  • 更多的信息需要点击名单上的姓名进入子页(下称为详情页)进行查看。需要循环点击,二级爬取。

思路

思路是我们通过名单页上的 XPath 选择和翻页,获得所有对应着每个人的详情页链接。之后遍历详情页链接,爬取信息,存为 csv 文件。

观察情况

按 F12 可以打开浏览器的开发者工具,选中 Network 页。

刷新名单页,可以看到信息流,包括 Request URL 和 Request Headers 等等。因为是密码登录的,我们在发送请求时需要 Cookie 作为头信息。

点开第二页名单页,我们发现翻页只需要修改 url 链接 page= 后面的内容:http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList?pinqing_dwdm=80002&is_yw=False&page=xxx

清空 Network 信息流,随意点一个名字,打开改名字对应的详情页。观察到,它的 Request URL 是:http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaEdit?zhuanjia_id=xxxxx。它以 get 的方式请求信息,且 zhuanjia_id 对应着每个人的 ID,只要改变这个 ID,就会返回不同人的信息。容易看到,ID 的信息,可以在名单页的姓名栏的 href 中找到。

尝试了,通过 pandas 的 pandas.read_html 读取网上的表格和基本的数据格式化操作在此处比较麻烦,故而,直接使用 XPath 定位。

爬取名单表

按照上述的思路,我们分两步走,先使用名单页,通过翻页汇总所有人对应的详情页信息。可以观察到的是

写好 Headers,通过 requests.get 返回网页信息,再利用 Xpath 进行提取之后存档。

代码如下:

import requests
from lxml import etree
import pandas as pd
# parameter = {
    
    
#             "key1":"value1",
#             "key2":"value2"
#             }
headers = {
    
    "Cache-Control": "max-age=0",
           "Connection":"keep-alive",
           "Cookie":"xxxxxxxxxx",
           "Referer":"http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList?pinqing_dwdm=80002&is_yw=False&page=2",
           "Upgrade-Insecure-Requests":"1",
           "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
    }
total_page = 2111
dfs = pd.DataFrame(columns=['name','id']);
ind = 0
for i in range(2111,total_page+1):
    response = requests.get("http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList? \
                            pinqing_dwdm=80002&is_yw=False&page="+str(i), \
                            headers = headers)#,params = parameter)
    # print(response.url)
    # print(response.text)
    # print(response.content)
    # print(response.encoding)
    # print(response.status_code)
    html=response.text
    tree=etree.HTML(html)
    NAME = tree.xpath('//*[@id="main-content"]/div/div/div/div/div[2]/div/table/tbody/tr/td[1]/a')
    IDHREF = tree.xpath('//*[@id="main-content"]/div/div/div/div/div[2]/div/table/tbody/tr/td[1]/a/@href')
    for j in range(0,len(NAME)):
        ind = ind+1
        print(ind)
        ids = str(IDHREF[j]).split('=')
        new = pd.DataFrame({
    
    'name':NAME[j].text, 'id':int(ids[1])},index=[ind])
        dfs=dfs.append(new) 
dfs.to_csv("./教授名字和ID.csv", encoding="utf_8_sig")

这里没有特别需要强调的地方,只不过这份代码不是写得非常高性能,有很多可以优化的地方,比如只要一个 tree.xpath。

爬取详情页二级信息

假设已经得到了爬取详情页需要 ID,这时候,我们可以通过不断地对微小区别的 url 们发送请求,就可以得到所以详情页信息。再通过开发者工具右键 XPath 拷贝工具,得到 XPath 匹配模式,粘贴提取信息,通过 Pandas 整理,存档。所用的代码如下:

import requests
from lxml import etree
import pandas as pd
ID1 = pd.read_csv('./教授名字和ID0.csv')
ID2 = pd.read_csv('./教授名字和ID.csv')
ID1n = list(map(int, ID1['id'])) 
ID2n = list(map(int, ID2['id'])) 
IDs = ID1n+ID2n
IDs.sort()

header = {
    
    "Connection": "keep-alive",
"Cookie": "xxxx",
"Host": "py.ucas.ac.cn",
"Referer": "http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList?pinqing_dwdm=80002&is_yw=False&page=1936",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
}
bg = 20617;
ed = len(IDs)
#ed = 2
data = pd.DataFrame(columns=['姓名','性别','单位','通讯地址','邮编','邮箱','手机',\
                             '固话','出生日期','身份证号','学科专业','方向','导师类别',\
                                 '专业技术职务','银行户头','银行地址','银行号码'])
count = -1
for i in range(bg,ed):
    count = count+1
    id = IDs[i]
    print(str(IDs[bg])+"TO"+str(IDs[ed-1])+":"+str(i)+":"+str(id)+"进行中...")
    response = requests.get("http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaEdit?zhuanjia_id="+str(id),headers=header)
    html=response.text
    # print(html)
    # with open('test.html','w',encoding='utf-8') as f:
    #     f.write(html)
    tree=etree.HTML(html)
    zjxm = str(tree.xpath('//*[@id="ZJXM"]/@value')[0]) #姓名
    zjxb = str(tree.xpath('//*[@id="ZJXB"]/option[@selected="selected"]/@value')[0])#性别
    jsdw = str(tree.xpath('//*[@id="DWDM"]/option[@selected="selected"]')[0].text)#单位
    zjtxdz = str(tree.xpath('//*[@id="ZJTXDZ"]/@value')[0])#通讯地址
    zjyzbm = str(tree.xpath('//*[@id="ZJYZBM"]/@value')[0])#邮编
    UserName = str(tree.xpath('//*[@id="UserName"]/@value')[0])#邮箱
    zjsj = str(tree.xpath('//*[@id="ZJSJ"]/@value')[0])#手机
    zjtel = str(tree.xpath('//*[@id="ZJTEL"]/@value')[0])#固话
    zjcsrq = str(tree.xpath('//*[@id="ZJCSRQ"]/@value')[0])#出生日期
    zjhm = str(tree.xpath('//*[@id="zjhm"]/@value')[0])#身份证号
    tmp = tree.xpath('//*[@id="ZJ_XKZY"]/option[@selected="selected"]')
    if len(tmp)==0:
        zjxkzy = ''
    else:
        zjxkzy = str(tmp[0].text)#学科专业
    yjfx = str(tree.xpath('//*[@id="yjfx"]/@value')[0])#方向  
    zjlb = str(tree.xpath('//*[@id="ZJLB"]/option[@selected="selected"]')[0].text)#导师类别
    tmp = tree.xpath('//*[@id="ZJZWCODE"]/option[@selected="selected"]')
    if len(tmp)==0:
        zjzwcode = ''
    else:
        zjzwcode = str(tmp[0].text)#专业技术职务  
    bank_hutou = str(tree.xpath('//*[@id="bank_hutou"]/@value')[0])#银行户头
    bank_dizhi = str(tree.xpath('//*[@id="bank_dizhi"]/@value')[0])#银行地址
    bank_xingming = str(tree.xpath('//*[@id="bank_xingming"]/@value')[0])#银行号码
    data.loc[count] = [zjxm,zjxb,jsdw,zjtxdz,zjyzbm,UserName,zjsj,zjtel,zjcsrq,zjhm,zjxkzy,yjfx,zjlb,zjzwcode,bank_hutou,bank_dizhi,bank_xingming]
data.to_csv("./"+str(bg)+"_"+str(IDs[bg])+"TO"+str(i)+"_"+str(IDs[i])+"高校教授联系方式.csv", encoding="utf_8_sig")

我们知道,长时间的爬取,总会出现一些问题,比如网络拒绝问题。在我的代码中个,没有任何的 try 方式的异常捕捉。我采用的异常处理方式时,代码跑断了,先把中间结果手动地存下来。再手动地从断点出手动地开启代码重跑。
我享受这种手动挡的过程,而不需要任何的异常处理机制。

爬虫请遵守相关法律法规,不要做违法犯罪的事情

爬虫写得好,牢饭吃到饱。不爬私有数据,不把网站搞崩。不要将爬虫方法用于任何的盈利。

爬虫小技巧总结

  1. Request 的 Headers 信息可以通过直接从浏览器开发者工具信息流里面拷贝修改得到。

  2. 匹配信息的时候建议存一份 Python 请求返回的网页进行分析,而不是在浏览器中直接访问请求地址,根据返回的结果做元素匹配。因为有时候 Python 请求的得到的结果和浏览器请求得到的结果是不一样的。存 Python 返回的网页结果的代码示例如下:

    response = requests.get("http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaEdit?zhuanjia_id="+str(id),headers=header)
    html=response.text
    print(html)
    with open('test.html','w',encoding='utf-8') as f:
    	f.write(html)
    
  3. 可以通过鼠标右键,检查元素,Copy XPath 得到 XPath 的匹配模式,黏贴到代码中接口。但寻找的结果为空或者不对的时候,可以对你要找的元素的姊妹单元复制 XPath,进行比对分析。

  4. 擅 XPath @ 功能。当我们要去 XPath 要取标签里面的元素的值的时候,可以用 @ 进行取值,如:tree.xpath('//*[@id="DWDM"]/option[@selected="selected"]')[0].text 此中中括号本表示索引,option[@selected="selected"] 表示在所有的 option 里面选择满足条件的(这里是下拉框产生的)。再比如 tree.xpath('//*[@id="ZJXM"]/@value')[0] 中 /@value 表示在所有 //*[@id="ZJXM"] 中取 value 的值,组成 list。

  5. 整理一份 Request+XPath 的模板如下,每次想要爬东西,复制修改即可用:

    import requests
    from lxml import etree
    import pandas as pd
    # parameter = {
          
          
    #             "key1":"value1",
    #             "key2":"value2"
    #             }
    header = {
          
          "Connection": "keep-alive",
    "Cookie": "xxx",
    "Host": "py.ucas.ac.cn",
    "Referer": "http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaList?pinqing_dwdm=80002&is_yw=False&page=1936",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
    }
    data = pd.DataFrame(columns=['姓名','单位'])
    response = requests.get("http://py.ucas.ac.cn/zh-cn/zhuanjia/ZhuanjiaEdit?zhuanjia_id=xxx",headers=header)#,params = parameter)
    html=response.text
    print(html)
    # print(response.url)
    # print(response.text)
    # print(response.content)
    # print(response.encoding)
    # print(response.status_code)
    with open('test.html','w',encoding='utf-8') as f:
        f.write(html)
    tree=etree.HTML(html)
    zjxm = str(tree.xpath('//*[@id="ZJXM"]/@value')[0]) #姓名
    jsdw = str(tree.xpath('//*[@id="DWDM"]/option[@selected="selected"]')[0].text)#单位
    data.loc[0] = [zjxm,jsdw]
    data.to_csv("xxx.csv", encoding="utf_8_sig")
    

猜你喜欢

转载自blog.csdn.net/lusongno1/article/details/128195834