V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
MrVito
V2EX  ›  Python

爬虫相关,关于 XPath 的疑问

  •  
  •   MrVito · 2022-09-14 18:38:09 +08:00 · 3075 次点击
    这是一个创建于 561 天前的主题,其中的信息可能已经有所发展或是发生改变。

    http://eid.csrc.gov.cn/fund/disclose/instance_html_view.do?instanceid=11733222 http://eid.csrc.gov.cn/fund/disclose/instance_html_view.do?instanceid=11733213

    在上面这两个链接中我想获取对应的基金名称,我在浏览器里面获取到的 full XPath 如下

    /html/body/table/tbody/tr/td[2]/table/tbody/tr[1]/td[1]/div/table[1]/tbody/tr[2]/td/div/div/table/tbody/tr[1]/td[2]/p
    

    但是第二个链接中我能顺利获取到值,第一个获取到的内容为空

    为了给大佬省事,对应的代码如下

    from ast import main
    from fake_useragent import UserAgent
    from lxml import etree
    import requests
    
    ua = UserAgent()
    headers = {"user-agent": ua.random}
    url1 = "http://eid.csrc.gov.cn/fund/disclose/instance_html_view.do?instanceid=11733222"
    url2 = "http://eid.csrc.gov.cn/fund/disclose/instance_html_view.do?instanceid=11733213"
    
    
    def get_fund_name(url: str):
        x = requests.get(url, headers = headers)
        selector = etree.HTML(x.content)
        fund_name = selector.xpath('/html/body/table/tbody/tr/td[2]/table/tbody/tr[1]/td[1]/div/table[1]/tbody/tr[2]/td/div/div/table/tbody/tr[1]/td[2]/p/text()')
        return fund_name
    
    print(get_fund_name(url1))
    print(get_fund_name(url2))
    

    output 如下

    []
    ['长信利盈灵活配置混合型证券投资基金']
    

    我仔细对比了这两个网页,实在是找不到原因……

    34 条回复    2022-09-19 19:40:23 +08:00
    dongtingyue
        1
    dongtingyue  
       2022-09-14 18:47:23 +08:00
    有些 dom 是 js 渲染的
    MrVito
        2
    MrVito  
    OP
       2022-09-14 18:49:34 +08:00
    @dongtingyue 你可以看下这个网页,应该是纯静态的
    Stoney
        3
    Stoney  
       2022-09-14 18:58:47 +08:00 via iPhone
    @MrVito get 里面加个 wait 设置等待时间?有时候是没来得及加载出来吧
    humbass
        4
    humbass  
       2022-09-14 19:03:54 +08:00
    2202 年的爬虫应该用 JS 来实现更强大;比如 Puppeteer
    tankren
        5
    tankren  
       2022-09-14 19:04:17 +08:00
    用 try+except 抛个异常看看,为啥要用绝对路径啊,不是不推荐吗
    MrVito
        6
    MrVito  
    OP
       2022-09-14 19:21:55 +08:00
    @tankren 相对路径我也取不到……所以就用绝对路径了
    MrVito
        7
    MrVito  
    OP
       2022-09-14 19:23:32 +08:00
    @Stoney 加了一个 timeout = 10 ,也还是不对……
    MrVito
        8
    MrVito  
    OP
       2022-09-14 19:24:18 +08:00
    @tankren 也没有抛异常出来……
    MrVito
        9
    MrVito  
    OP
       2022-09-14 19:24:38 +08:00
    @humbass 对 js 不熟……
    zengxs
        10
    zengxs  
       2022-09-14 19:26:19 +08:00   ❤️ 1
    试试这个

    //*[contains(text(), '基金名称')]/../../td[2]/p/text()
    MrVito
        11
    MrVito  
    OP
       2022-09-14 19:27:47 +08:00
    @zengxs 卧槽,你这个确实可以,这个思路好像也是对的。但是我还是想不明白我那种为啥不行……
    zengxs
        12
    zengxs  
       2022-09-14 19:29:20 +08:00
    @MrVito 因为两个页面的 html 内容不一样,你需要找到二者之间相同的特征才行
    MrVito
        13
    MrVito  
    OP
       2022-09-14 19:31:56 +08:00
    @zengxs 问题是我用的 full xpath ,从浏览器提取出来的这两个页面的都是相同的呀?
    zengxs
        14
    zengxs  
       2022-09-14 19:35:28 +08:00
    @MrVito 那可能是这个页面的 html 不规范,导致浏览器和 lxml 解析出来的 dom 不一样
    humbass
        15
    humbass  
       2022-09-14 20:24:26 +08:00
    网页是 JS 写的,爬虫当然是 JS 来写解析 DOM 结构也是天然的! Python 是过去式了;

    ```
    const jsdom = require('jsdom')
    const axios = require('axios')
    ;(async () => {
    const url =
    'http://eid.csrc.gov.cn/xbrl/REPORT/HTML/2022/FC190100/CN_50470000_009421_FC190100_20220039/CN_50470000_009421_FC190100_20220039.html'
    const response = await axios.get(url)
    const result = new jsdom.JSDOM(response.data)
    const table = result.window.document.querySelector('#tabItem0')
    const title = table.querySelectorAll('p')[1].textContent
    console.log('title:', title)
    })()

    ``
    zjuster
        16
    zjuster  
       2022-09-14 20:44:36 +08:00
    如果 Xpath 写的绝对路径太多了,可能要比较一下两个页面(虽然链接一致),但在 Dom 结构可能不一致的情况。
    写的宽泛一点试试看。
    wxf666
        17
    wxf666  
       2022-09-14 20:49:09 +08:00
    @humbass 问一下,看起来都是调包,Python 是有啥明显的坑吗?
    humbass
        18
    humbass  
       2022-09-14 21:14:13 +08:00
    @wxf666 也不是说 Python 有坑,而是网页解析这玩意本身就是 JS 的领域。
    zengxs
        19
    zengxs  
       2022-09-14 21:31:48 +08:00 via iPhone
    @humbass 这个是和底层的 html parser 有关,和语言关系不大,jsdom 用的 html parser 和浏览器的也不一样
    wxf666
        20
    wxf666  
       2022-09-14 21:32:15 +08:00
    @humbass js 本身也没有啥特别的语言特性,是用于描述 DOM 的吧

    你是想说,无头浏览器能方便地,执行一些自己的 js 代码?
    humbass
        21
    humbass  
       2022-09-14 21:37:09 +08:00
    @wxf666 OP 主贴出来的需求,也用不到无头浏览器,简单的访问就可以了,
    @zengxs 对比 python 的 xpath 和 document.querySelector 这样的语法,肯定是后者方便啊。
    wxf666
        22
    wxf666  
       2022-09-14 21:48:47 +08:00
    @humbass xpath 不是 Python 的,只是 lxml 库的一个功能。这个库好像不支持 css 风格的查询

    换成 BeautifulSoup 库就支持了:xxx.select('#tabItem0')

    另外,我还是觉得 xpath 更方便描述 xml html
    zengxs
        23
    zengxs  
       2022-09-14 22:07:56 +08:00 via iPhone
    @humbass 呃,xpath 和 css selector 只是两种不同的 html 查询方式而已,和语言无关,python 也可以 css selector 查询,js 也可以 xpath 查询

    至于哪个更方便这个也不好说,css selector 语法简单是牺牲了很多功能换来的
    bavtoex
        24
    bavtoex  
       2022-09-15 00:16:35 +08:00
    @wxf666 +1,美丽汤的 xxx.select('#tabItem0') 很灵活
    webcape233
        25
    webcape233  
       2022-09-15 02:05:22 +08:00 via iPhone
    这简单查询观察下写正则可能更快
    mscststs
        26
    mscststs  
       2022-09-15 02:47:57 +08:00
    看到 tbody 就要注意了,浏览器会自动在 table 里面自动填充一级 tbody ,这就是为什么你取 Xpath 是一样的,实际上你右键查看源码,就能看出来,第一个链接的 html 没有在源码里面手写 tbody ,而第二个写了。
    ADMlN
        27
    ADMlN  
       2022-09-15 05:02:37 +08:00
    xpath 改成'/html/body/table/tbody',第一个为空,第二个有值
    brucmao
        28
    brucmao  
       2022-09-15 09:16:19 +08:00
    //p[contains(text(),'基金名称')]/../following-sibling::td/p

    页面复制的经常不准确,可以试试 xpath 轴
    https://developer.mozilla.org/en-US/docs/Web/XPath

    另外可以用浏览器插件 SelectorsHub 辅助
    JieGaLee
        29
    JieGaLee  
       2022-09-15 09:32:27 +08:00
    遇到 tbody 就要去源码里确认一下是不是真的有 tbody 。
    hidie
        30
    hidie  
       2022-09-15 10:00:33 +08:00
    不要写 tbody
    fbichijing
        31
    fbichijing  
       2022-09-15 17:03:12 +08:00
    一个有 tbody 一个没有,所以找不到。
    这么深层数的筛选器加上那么多的位置 index ,简直是独木桥中的独木桥啊。
    ijustdo
        32
    ijustdo  
       2022-09-15 18:15:09 +08:00
    //div[@id="con_one_1"][1]/descendant::table/descendant::tr[1]/td[2]/p/text()
    chunhai
        33
    chunhai  
       2022-09-19 19:36:35 +08:00
    我一般都用正则来取
    chunhai
        34
    chunhai  
       2022-09-19 19:40:23 +08:00
    @chunhai
    ```
    <td align="left" colspan="2"><p>基金名称<\/p><\/td>\s*<td align="left" width="60%" colspan="3">\s*<p>(.*)<\/p>
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1236 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 18:02 · PVG 02:02 · LAX 11:02 · JFK 14:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.