【问题标题】:Parse json file from website with python使用python从网站解析json文件
【发布时间】:2017-04-03 21:48:49
【问题描述】:

我希望解析并保存嵌入在 html 代码中的 json 文件的内容。但是,当我隔离相关字符串并尝试使用 json 包加载它时,我收到错误 JSONDecodeError: Extra data 并且我不确定是什么原因造成的。

有人建议相关代码实际上可能包含多个字典,这可能会出现问题,但如果这是真的,我不清楚如何进行。下面提供了我的代码。任何建议都非常感谢!

from bs4 import BeautifulSoup
import urllib.request 
from urllib.request import HTTPError
import csv
import json
import re

def left(s, amount):
    return s[:amount]

def right(s, amount):
    return s[-amount:]

def mid(s, offset, amount):
    return s[offset:offset+amount]
url= "url"
from urllib.request import Request, urlopen
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
try:
    s = urlopen(req,timeout=200).read()
except urllib.request.HTTPError as e:
    print(str(e))  
soup = BeautifulSoup(s, "lxml")
tables=soup.find_all("script")
for i in range(0,len(tables)):
    if str(tables[i]).find("TimeLine.init")>-1:
        dat=str(tables[i]).splitlines()
        for tbl in dat:
            if str(tbl).find("TimeLine.init")>-1:
                s=str(tbl).strip()
j=json.loads(s)

【问题讨论】:

  • 如果它包含多个字典,您需要一次解析它们。如果不进行一些巧妙的解析,很难将字符串分解成与之对应的片段。每次都失败吗?它失败的字符串是什么? s 在调用json.loads 之前是什么样子的?
  • s 很长 - 大约 50k 个字符,因此无法完整发布。虽然会添加提取物
  • 虽然它并不直接致力于解决您的技术问题。解析网站内容以获取内部数据很少是一个好主意。主要是因为你are not allowed to do so,但也因为它可能会改变。
  • @dahrens,怎么会这样?这些条款的第 7 点允许他出于个人、非商业用途这样做。内容可能会改变是网络的本质。
  • “任何建议”过于宽泛。

标签: python json web-scraping beautifulsoup


【解决方案1】:

您可以使用 JSON 自己的异常报告来帮助解析,它给出了 loads() 失败的位置,例如:

Extra data: line 1 column 1977 (char 1976)

以下脚本首先定位所有 javascript <script> 标签并在每个标签中查找函数。然后它会找到 JSON 文本的外部开始和结束。然后,它会尝试对其进行解码,记录失败的偏移量,跳过该字符并再次尝试。当找到最终块时,它将成功解码。然后它在每个有效块上调用loads(),将结果存储在json_decoded

from bs4 import BeautifulSoup
from urllib.request import HTTPError, Request, urlopen
import csv
import json

url = "url"
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})

try:
    s = urlopen(req, timeout=200).read()
except urllib.request.HTTPError as e:
    print(str(e))  

json_decoded = []
soup = BeautifulSoup(s, "lxml")

for script in soup.find_all("script", attrs={"type" : "text/javascript"}):
    text = script.text
    search = 'FieldView.TimeLine.init('
    field_start = text.find(search)

    if field_start != -1:
        # Find the start and end of the JSON in the function
        json_offsets = []
        json_start = field_start + len(search)
        json_end = text.rfind('}', 0, text.find(');', json_start)) + 1

        # Extract JSON
        json_text = text[json_start : json_end]

        # Attempt to decode, and record the offsets of where the decode fails
        offset = 0

        while True:
            try:
                dat = json.loads(json_text[offset:])
                break
            except json.decoder.JSONDecodeError as e:
                # Extract failed location from the exception report
                failed_at = int(re.search(r'char\s*(\d+)', str(e)).group(1))
                offset = offset + failed_at + 1
                json_offsets.append(offset)

        # Extract each valid block and decode it to a list
        cur_offset = 0

        for offset in json_offsets:
            json_block = json_text[cur_offset : offset - 1]
            json_decoded.append(json.loads(json_block))
            cur_offset = offset

print(json_decoded)

这导致json_decoded 持有两个 JSON 条目。

【讨论】:

    【解决方案2】:

    您正在尝试解析如下所示的字符串:

    FieldView.TimeLine.init(,,, true, 4, "29:58", 1798);上一页>
    
    

    尖括号 在这里只是用来分组,它们没有特殊含义,实际上并不存在。

    您将无法正确解析它,因为它不是有效的 json。 相反,剥离函数调用并添加例如方括号将函数的参数包装到一个 json 数组中。

    json.loads("[{:s}]".format(str(dat[4]).strip()[24:-2])
    

    【讨论】:

    • 顺便说一句,这是可行的,但我真的不明白如何。介意解释吗?特别是“[{:s}]”部分?
    • 引号分隔字符串中的{:s} 是一个占位符:format 将使用其参数中给出的字符串填充它。参见例如hereofficial documentation。该占位符周围的方括号只是将这些字符添加到字符串中,因此字符串看起来像 JSON 对象的 JSON 数组。 [24:-2] 部分用于获取字符串的子字符串,以便您在 inside javascript 函数调用中得到参数。
    猜你喜欢
    • 1970-01-01
    • 2021-06-11
    • 2012-07-18
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2015-12-25
    • 1970-01-01
    相关资源
    最近更新 更多