本篇博客的背景很简单,大学舍友因为技术不行,没找到对口开发岗,所以开了个公司,现在只能招程序员干活了。😇

某哥:你看,技术不行,好的程序员都招不到!
擦姐:…… 你的关注点难道不是“他开公司了吗?”

⛳️ 实战场景

这次的需求来源是大学同学,他需要采集一些专接本院校资料,所以只能求助我这个 会写爬虫的工程师 了。
项目费用依旧是 【答应请吃一顿饭】

通过对目标站点的分析,得到下图所示采集流程,点击右侧学校名称,跳转到详情内页,获取该学校专接本开设专业,以及专业基础课,专业课,还有并不准确的招生计划数。

Python程序员私活来源:大学同窗开公司,伸手就要爬资料

由于本私活没有费用,所以我们使用 requests + lxml 模块进行简单实现即可。

⛳️ 编码环节

通过开发者工具分析目标数据所在的 DOM 元素,进行直接罗列,如下所示:

<li>
  <a href="http://www.hebzjb.cn/sjztd/">
    <img
      src="/uploadfile/2020/0813/thumb_120_120_20200813021226445.jpg"
      alt="石家庄铁道大学"
    />
    <span>石家庄铁道大学</span></a
  >
</li>

所有的目标 li 都包含在 classschool-listul 中,所以在编码时,第一步要获取该 ul 标签,然后通过迭代依次获取子标签 ahref 属性,子标签 span 的内部文本。

import requests
from lxml import etree

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"}

res = requests.get('http://www.hebzjb.cn/jbyx/', headers=headers)
data = etree.HTML(res.text)
school_a = data.xpath('//ul[@class="school-list"]/li')

for school_li in school_a:
    detail_url = school_li.xpath('./a/@href')[0]
    name = school_li.xpath('./a/span/text()')[0]
    print(name, detail_url)

学校页解析代码如上。接下来就可以通过详情页地址变量 detail_url 进入内页,获取专业相关数据。

在继续编写前,将上述代码进行函数重写,改写之后的完整代码如下所示,临摹之后希望对你有借鉴意义。

import requests
from lxml import etree


# 获取目标站点源码
def request_html(url):
    headers = {
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"}

    res = requests.get(url, headers=headers)
    data = etree.HTML(res.text)
    return data


# 获取详情页地址
def get_detail_url():
    url = 'http://www.hebzjb.cn/jbyx/'
    data = request_html(url)
    school_a = data.xpath('//ul[@class="school-list"]/li')
    ret = []
    for school_li in school_a:
        detail_url = school_li.xpath('./a/@href')[0]
        name = school_li.xpath('./a/span/text()')[0]
        ret.append({
            "name": name,
            "detail": detail_url
        })
    return ret


if __name__ == '__main__':
    format_school = get_detail_url()
    print(format_school)

⛳️ 获取内页数据

继续获取内页数据,此处代码仅编写一页数据的采集,其余页面的采集遵循相同规则。

if __name__ == '__main__':
    # format_school = get_detail_url()
    # print(format_school)
    data = request_html('http://www.hebzjb.cn/sjztd/')
    school_table = data.xpath('//table[@class="school-table"]')[0]

    tr_list = school_table.xpath("./tbody/tr")
    for tr in tr_list:
        td1 = tr.xpath("./td[1]/a/text()")  # 获取专业
        td2 = tr.xpath("./td[2]/text()")  # 获取专业基础课
        td3 = tr.xpath("./td[3]/ul/li/text()")  # 获取专业课
        td4 = tr.xpath("./td[4]/ul/li/text()")  # 获取计划人数

在上述代码中,最重要和最需要掌握的就是 xpath 语法获取目标元素,尤其是 td[序号] 该类写法,实践过程中很容易出现获取到空数据,或者全部是换行符 \n 的场景,此时的解决方案就是排查 xpath 语法是否编写正确,有时还需要借助浏览器的 XHelper 插件进行测试。

案例进行到这里,数据已经保存到本地了,伸手要数据的老板同学交代的任务已经完成。

最后在整理一下 XPath 表达式在本案例中实践的相关内容:

一个反斜杠 /,表示从根节点获取,在迭代数据的时候,希望从当前节点获取数据,需要配合点号(.)使用,双反斜杠表示从文档中选择节点,不需要考虑层级关系,例如前文代码中获取 li 标签,直接从文档进行选择,使用的 XPath 表达式为 //ul[@class="school-list"]/li,即选择文档中 classschool-listul 标签,如果存在,全部提取,所以得到的结果是一个列表,由于我们提前检查了页面中仅存在一个可匹配到的 ul 标签,所以直接获取列表索引为 0 的元素即可。

XPath 谓语表达式,即基于位置获取元素,在本文编码中也进行了实践,代码示例是 ./td[1]/a/text(),翻译成中文就是获取当前节点下第一个 td 标签内的文本,该知识点需要牢记,类似的谓语表达式还有如下内容。

  • /父标签/子标签[1]:基于位置获取子元素(标签);
  • /父标签/子标签[last()]:获取最后一个子元素;
  • /父标签/子标签[last()-1]:获取倒数第二个子元素;
  • /父标签/子标签[position()<3]:获取父元素中的前两个子元素。

📢📢📢📢📢📢
💗 你正在阅读 【梦想橡皮擦】 的博客
👍 阅读完毕,可以点点小手赞一下
🌻 发现错误,直接评论区中指正吧
📆 橡皮擦的第 736 篇原创博客

从订购之日起,案例 5 年内保证更新

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐