尔加。我花了相当多的时间试图找到如何做到这一点,无论是正确甚至是黑客,我只是难倒。我有2500多个HTML文件,我已经从网站上下载了,我只需要从任何给定页面中提取有限数量的信息:页面描述的谈话标题(所以我可以整理这些数据)我们已经拥有的一个巨大的CSV,然后是给出一个特定讲话的事件,以及发表讲话的日期。
这些网页的HTML非常庞大,并且填充了<script>
个元素。我只想要一个q
后面的那个。启动此块的行如下所示:
<script>q("talkPage.init", {
以下是相当多的数据。我只需要看起来像这样的三个项目:
"event":"TEDGlobal 2005",
"filmed":1120694400,
"published":1158019860,
幸运的是,"filmed"
和"published"
只在此大块中出现一次,但"event"
多次出现。它始终是一样的,所以我不关心这些脚本中的哪一个抓取。
我的想法是使用 BeautifulSoup 来查找<script>q
元素,然后将其传递到 json 模块进行解析,但我无法弄清楚告诉汤抓住<script>
元素后跟aq - 类和id很容易。接下来......不是那么多。
要开始处理JSON部分,我已经创建了一个文本文件,其中只包含<script>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__":
这就是我现在所处的位置。任何可以在汤或 json 上完成任务以完成此任务的灯光将非常感激。
答案 0 :(得分:3)
在不了解完整背景的情况下,这是一个穷人的尝试:
假设您的html看起来像这样:
new Map(Object.entries(object))
您可以这样编码:
<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>
然后你可以开始解释jsons:
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.
这里有很多假设。假设print(my_jsons[0]['event'])
print(my_jsons[0]['filmed'])
print(my_jsons[0]['published'])
# Output:
# TEDGlobal 2005
# 1120694400
# 1158019860
元素中的所有文字始终以<script>q
开头,以q("talkPage.init",
结尾。此外,它假设返回的文本遵循json格式进行下一阶段的解析。我还假设你知道如何解析json结果。
答案 1 :(得分: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库来解析它,或者将最后的东西与你想要的东西相匹配。由于filmed
和published
是唯一的,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东西的库,但我不太熟悉它。
答案 2 :(得分:0)
这是我最终使用的脚本,非常感谢@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