【问题标题】:Python:Getting text from html using BeautifulsoupPython:使用 Beautifulsoup 从 html 获取文本
【发布时间】:2016-03-24 00:10:12
【问题描述】:

我正在尝试从此链接link example: kaggle user ranking no1 中提取排名文本编号。图像更清晰:

我正在使用以下代码:

def get_single_item_data(item_url):
    sourceCode = requests.get(item_url)
    plainText = sourceCode.text
    soup = BeautifulSoup(plainText)
    for item_name in soup.findAll('h4',{'data-bind':"text: rankingText"}):
        print(item_name.string)

item_url = 'https://www.kaggle.com/titericz'   
get_single_item_data(item_url)

结果是None。问题是soup.findAll('h4',{'data-bind':"text: rankingText"}) 输出:

[<h4 data-bind="text: rankingText"></h4>]

但在检查时链接的 html 中是这样的:

<h4 data-bind="text: rankingText">1st</h4>。可以在图片中看到:

很明显,缺少文本。我怎样才能超越它?

编辑: 在终端打印soup 变量我可以看到这个值存在:

所以应该有一种方法可以通过soup 访问。

编辑 2:我尝试使用来自 stackoverflow question 的投票最多的答案,但没有成功。可能是那里的解决方案。

【问题讨论】:

  • 当我检查该页面的来源时,它看起来像 <h4 data-bind="text: rankingText"></h4> ... 所以结果似乎是正确的。
  • 我已经编辑了我的问题。您可以在屏幕截图中看到该值存在。
  • 代码中的 url 与链接示例中的不同;这令人困惑。我建议更改代码中的那个以匹配示例,反之亦然。
  • 感谢您的观察。已编辑。

标签: python html beautifulsoup html-parsing kaggle


【解决方案1】:

我已经在纯文本上使用正则表达式解决了您的问题:

def get_single_item_data(item_url):
    sourceCode = requests.get(item_url)
    plainText = sourceCode.text
    #soup = BeautifulSoup(plainText, "html.parser")
    pattern = re.compile("ranking\": [0-9]+")
    name = pattern.search(plainText)
    ranking = name.group().split()[1]
    print(ranking)

item_url = 'https://www.kaggle.com/titericz'
get_single_item_data(item_url)

这仅返回排名数字,但我认为它会对您有所帮助,因为从我看到的 rankText 只需在数字右侧添加“st”、“th”等

【讨论】:

    【解决方案2】:

    如果您不打算按照@Ali 的建议通过selenium 尝试浏览器自动化,则必须解析包含所需信息的javascript。你可以用不同的方式做到这一点。这是一个工作代码,它通过regular expression pattern 定位script,然后提取profile 对象,将其与json 一起加载到Python 字典中并打印出所需的排名:

    import re
    import json
    
    from bs4 import BeautifulSoup
    import requests
    
    
    response = requests.get("https://www.kaggle.com/titericz")
    soup = BeautifulSoup(response.content, "html.parser")
    
    pattern = re.compile(r"profile: ({.*}),", re.MULTILINE | re.DOTALL)
    script = soup.find("script", text=pattern)
    
    profile_text = pattern.search(script.text).group(1)
    profile = json.loads(profile_text)
    
    print profile["ranking"], profile["rankingText"]
    

    打印:

    1 1st
    

    【讨论】:

    • 请问re.compile函数中re.MULTILINE | re.DOTALL的作用是什么?
    • @MpizosDimitris 这些只是正则表达式标志 - MULTILINE 启用多行搜索,DOTALL 允许 . 也匹配多行字符。
    【解决方案3】:

    数据是使用 javascript 进行数据绑定的,正如“data-bind”属性所暗示的那样。

    但是,如果您使用例如wget,您会看到在初始加载时,rankingText 值实际上存在于该脚本元素中:

    <script type="text/javascript"
    profile: {
    ...
       "ranking": 96,
       "rankingText": "96th",
       "highestRanking": 3,
       "highestRankingText": "3rd",
    ...
    

    所以你可以改用它。

    【讨论】:

    • highestRankingrankingText 不同吗?
    • 是的,谢谢,highestRanking 是 1,而rankingText 是“1st”。似乎它实际上是他应该寻找的“rankingText”。这完全基于对 HTML 的快速浏览。
    【解决方案4】:

    这可能是因为动态数据填充。

    一些javascript代码,页面加载后填写此标签。因此,如果您使用请求获取 html,则它尚未填充。

    <h4 data-bind="text: rankingText"></h4>
    

    请查看Selenium web driver。使用此驱动程序,您可以获取完整页面并正常运行 js。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-02
      • 2015-01-03
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      相关资源
      最近更新 更多