2016-11-20 7 views
0

私は、htmlコードに埋め込まれているjsonファイルの内容を解析して保存します。しかし、関連する文字列を分離してjsonパッケージでロードしようとすると、エラーJSONDecodeError: Extra dataが表示され、何が原因なのか不明です。pythonでウェブサイトからjsonファイルを解析する

関連するコードに実際に複数の辞書が含まれている可能性があり、問題がある可能性がありますが、これが当てはまる場合は進める方法がわかりません。私のコードは以下の通りです。どんな提案も高く評価されました!

あなたはこのように見える文字列を解析しようとしている
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) 
+0

複数の辞書が含まれている場合は、一度に1つずつ解析する必要があります。賢明な解析をせずに、文字列をそれらに対応する部分に分割するのは難しいです。毎回失敗しますか?それは正確に失敗する文字列は何ですか? '' json.loads'が呼び出される前に 's'はどのように見えますか? –

+0

はかなり長く、約50k文字ですので、完全に投稿することはできません。あなたの技術的な問題に直接的には捧げられていませんが、 – user3725021

+0

でも抽出は追加されます。内部データのためにウェブサイトコンテンツを解析することはめったに良い考えではありません。主にあなたが[許可されていない]ため(http://www.heraldsun.com.au/help/termsconditions)、変更される可能性があるためです。 – dahrens

答えて

1

あなたは、たとえば、loads()が失敗した場所の位置を与え解析を支援するためにJSONの独自の例外報告を使用することができます。

Extra data: line 1 column 1977 (char 1976) 

次のスクリプトは、最初にすべてのjavascript <script>タグを探し出し、それぞれの内部の関数を探します。次に、JSONテキストの外側の開始点と終了点を見つけます。これを使ってデコードを試み、失敗したオフセットに注意し、この文字をスキップして再試行します。最後のブロックが見つかると、それは正常に解読されます。その後json_decodedに結果を格納、有効な各ブロックにloads()を呼び出します。

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) 

これは、2つのJSONエントリを保持json_decodedになります。

+0

ありがとうございます - これは治療法です! – user3725021

1

FieldView.TimeLine.init(<first parameter - a json array>, <second parameter - a json array>, <third parameter, a json object>, true, 4, "29:58", 1798);

の角括弧、<と>、ここでしかグループに奉仕し、彼らは特別な意味を持たず、実際にはありませんプレゼント。

有効なJSONではないため、正しく解析できません。 代わりに、関数呼び出しを削除して、例:関数のパラメータをjson配列にラップします。

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

btwこれは動作しますが、私は実際にどのように理解できません。説明するケア?特に「[{:s}」の部分は? – user3725021

+0

引用符で区切られた文字列の中の '{:s}'はプレースホルダです: 'format' 引数に与えられた文字列で埋めてください。例えば、 [こちら](https://pyformat.info/)または [公式ドキュメント](https://docs.python.org/2/library/string.html#format-string-syntax) そのプレースホルダーを囲む角括弧 は、文字列にJSONオブジェクトのJSON配列のように見えるように、それらの文字を文字列に追加するだけです。 '[24:-2]'部分は文字列の部分文字列を取得するために使用されるので、javascript関数呼び出しの中で*のパラメータになります。 –

関連する問題