【问题标题】:Python find JSON in HTML and get select valuesPython 在 HTML 中查找 JSON 并获取选择值
【发布时间】:2018-06-28 00:33:51
【问题描述】:

呸。我花了相当多的时间试图找到如何正确甚至是骇人听闻地做到这一点,我只是被难住了。我从一个站点下载了 2500 多个 HTML 文件,我只需要从任何给定页面提取有限数量的信息:页面描述的谈话标题(所以我可以将这些数据与一个巨人CSV 我们已经有了),然后是给定演讲的事件,以及演讲的发布日期。

这些页面的 HTML 非常庞大,并且充满了 <script> 元素。我只想要后面跟着q 的那个。开始此块的行如下所示:

<script>q("talkPage.init", {

接下来是相当多的数据。我只需要看起来像这样的三个项目:

"event":"TEDGlobal 2005",
"filmed":1120694400,
"published":1158019860,

幸运的是,"filmed""published" 在这个大块中只出现一次,但"event" 出现了多次。它总是一样的,所以我不在乎任何脚本抓取其中的哪一个。

我的想法是使用 BeautifulSoup 找到 &lt;script&gt;q 元素,然后将其传递给 json 模块进行解析,但我不知道要告诉 strong>soup 来获取 &lt;script&gt; 元素,然后是 aq —— 类和 id 很容易。其次是……没那么多。

为了开始处理 JSON 部分,我创建了一个文本文件,其中只有 &lt;script&gt;q 元素的内容,但我承认让 json 模块来加载它工作不太好。

我的实验代码首先加载带有我感兴趣的 JSON 块的文本文件,然后尝试对其进行解码,以便我可以用它做其他事情:

import json

text = open('dawkins_script_element.txt', 'r').read()
data = json.loads(text)

但显然 JSON 解码器不喜欢我所拥有的,因为它会抛出 ValueError: Expecting value: line 1 column 1 (char 0)。呸!

此脚本元素的前三行如下所示:

<script>q("talkPage.init", {
"el": "[data-talk-page]",
"__INITIAL_DATA__":

这就是我目前所处的位置。任何可以在 soupjson 上完成此任务的信息将不胜感激。

【问题讨论】:

    标签: python json beautifulsoup


    【解决方案1】:

    在不了解完整上下文的情况下,这是一个穷人的尝试:

    假设你的 html 看起来像这样:

    <script>foo</script>
    <script>bar</script>
    <script>q("talkPage.init",{
    "foo1":"bar1",
    "event":"TEDGlobal 2005",
    "filmed":1120694400,
    "published":1158019860,
    "foo2":"bar2"
    })</script>
    <script>q("talkPage.init",{
    "foo1":"bar1",
    "event":"foobar",
    "filmed":123,
    "published":456,
    "foo2":"bar2"
    })</script>
    <script>foo</script>
    <script>bar</script>
    

    你可以这样编码:

    res = requests.get(url) # your link here
    soup = bs4.BeautifulSoup(res.content)
    my_list = [i.string.lstrip('q("talkPage.init", ').rstrip(')') for i in soup.select('script') if i.string and i.string.startswith('q')]
    
    # my_list should now be filled with all the json text that is from a <script> tag followed by a 'q'
    # note that I lstrip and rstrip on the script based no your sample (assuming there's a closing bracket), but if the convention is different you'll need to update that accordingly.
    
    #...#
    my_jsons = []
    for json_string in my_list:
        my_jsons.append(json.loads(json_string))
    
    # parse your my_jsons however you want.
    

    然后你就可以开始解释json了:

    print(my_jsons[0]['event'])
    print(my_jsons[0]['filmed'])
    print(my_jsons[0]['published'])
    
    # Output:
    # TEDGlobal 2005
    # 1120694400
    # 1158019860
    

    这里有很多假设。假设您在&lt;script&gt;q 元素中的所有文本将始终以q("talkPage.init", 开头并以) 结尾。此外,它假设返回的文本遵循 json 格式,用于下一阶段的解析。我还假设您了解如何解析 json 结果。

    【讨论】:

    • 我正在使用这两个答案,看看哪一个效果最好。明天第一件事我会有更多。感谢您如此周到的回答!
    • 解决这个问题,我必须使用soup = BeautifulSoup(text, "html5lib"),因为当我尝试soup = BeautifulSoup(text.content) 时,我得到AttributeError: 'str' object has no attribute 'content'。然后我使用列表推导,它产生一个长列表,但它有我们想要的部分。现在,我在 JSON 解析上磕磕绊绊,所以这就是我早上要花时间做的事情。谢谢!
    • 这取决于您的原始内容是什么 - 如果它是 request 对象(在我的示例中),那么您将需要获取文本 content 以供 BeautifulSoup 解析。在您的情况下,您的对象似乎是 str 对象,因此只需传递 str 本身就足够了。如果您在解析 json 时遇到问题,我建议您在 json module 上获取更多信息。
    • 我确实知道 JSON 模块是多么的繁琐。有些人可能会觉得这很难看,但为了让 JSON 模块解析字符串,我必须这样做:pre_json = '{"' + "".join(my_list) 然后my_json = json.loads(pre_json)
    • @JohnLaudun 听起来更像是传递给 json 模块的字符串格式不正确。您需要确保从 html 解析的字符串结果可以作为字典读取。如果它缺少{,那么它可能已被错误地从 html 解析中剥离。
    【解决方案2】:

    您可以使用正则表达式来匹配您想要的部分。

    import re
    # Filters the script-tag all the way to end ')' of q.
    scipt_tag = re.findall(r'<script>q\((?s:.+)\)', t)
    json_content = re.search(r'(?<=q\()(?s:.+)\)', script_tag[0]).group()
    json_content = json_content[:-1]  # Strip last ')'
    

    要找到你需要的东西,你可以使用 pythons json 库来解析它,或者将最后的东西与你想要的匹配。因为filmedpublished 是独一无二的,event 没有区别(据我了解?)

    import json
    json_content = json.loads(json_content)
    json_content['event']  # or whatever
    

    def get_val(a):
    re.search('r(?<=' + a + r'\": )(.+)').group(0)
    

    后者需要稍作过滤以删除尾随的]" 和前面的"[,或者你不想要的东西。

    听说beautifulsoup 也是一个很好的匹配html-stuff 的库,但我不是很熟悉。

    【讨论】:

    • 我喜欢使用regex 执行此操作的想法,这总是超出我的理解范围,但script_tag = re.findall(r'&lt;script&gt;q\((?s:.+)\)', text) 行抛出了error: unknown extension。看起来它不喜欢右括号? 739 if char == ")": 740 break --&gt; 741 raise error("unknown extension")
    【解决方案3】:

    这是我最终使用的脚本,非常感谢@Idlehands 和@Three。为了接触到奇怪的单引号 JSON,我将整个 JSON 元素读入一个列表,用逗号分隔。这是一个黑客,但它主要是有效的。

    def get_metadata(the_file):
    
        # Load the modules we need
        from bs4 import BeautifulSoup
        import json
        import re
        from datetime import datetime
    
        # Read the file, load it into BS, then grab section we want
        text = the_file.read()
        soup = BeautifulSoup(text, "html5lib")
        my_list = [i.string.lstrip('q("talkPage.init", {\n\t"el": "[data-talk-page]",\n\t "__INITIAL_DATA__":')
                   .rstrip('})')
                   for i in soup.select('script') 
                   if i.string and i.string.startswith('q')]
    
        # Read first layer of JSON and get out those elements we want
        pre_json = '{"' + "".join(my_list)
        my_json = json.loads(pre_json)
        slug = my_json['slug']
        vcount = my_json['viewed_count']
        event = my_json['event']
    
        # Read second layer of JSON and get out listed elements:
        properties = "filmed,published" # No spaces between terms!
        talks_listed = str(my_json['talks']).split(",")
        regex_list = [".*("+i+").*" for i in properties.split(",")]
        matches = []
        for e in regex_list:
            filtered = filter(re.compile(e).match, talks_listed)
            indexed = "".join(filtered).split(":")[1]
            matches.append(indexed)
        filmed = datetime.utcfromtimestamp(float(matches[0])).strftime('%Y-%m-%d')
        # published = datetime.utcfromtimestamp(float(matches[1])).strftime('%Y-%m-%d')
        return slug, vcount, event, filmed, #published
    

    【讨论】:

      猜你喜欢
      • 2022-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-11
      • 2012-12-25
      • 1970-01-01
      相关资源
      最近更新 更多