使用BeautifulSoup读取<script>内容
我一直在尝试读取一个网站的源代码,使用了以下代码:
import urllib2
from BeautifulSoup import BeautifulSoup
url = 'http://www.myurl.com/'
headers = {'User-Agent' : 'Mozilla/5.0'}
request = urllib2.Request(url,None,headers)
soup = BeautifulSoup(urllib2.urlopen(request).read())
我进一步缩小范围,使用了 scriptResults = soup('script',{'type' : 'text/javascript'})
。它的内容示例如下:
scriptResults = [<script type="text/javascript"> ... </script>,
...,
<script type="text/javascript">
//comment 1
$(function () {
//comment 2
var True = true, False = false;
func.start({
token1 : "...",
token2 : [...],
...
tokenN : ["value1","value2",...,"valueK"],
...
})
})
</script>,
...
]
现在我想提取 tokenN
的值。我知道这个值在整个文档中是唯一的,并且在我尝试读取的所有网页中都存在。此外,scriptResults
中的结果数量可能会变化,token 的数量也可能不同,所以我不能用位置索引来访问它们。同时,我意识到 BeautifulSoup
是一个 HTML 解析器,无法解析 JavaScript。那么,我该如何使用正则表达式来提取这些信息呢?
如果没有简单的方法可以获取 所有 的值,以下方法可以作为折中方案。大多数 的 values
形式是 "string1/xxxx/string2"
,其中 xxxx
是一些随机的 SHA 哈希,每个都不一样,我可以通过其他方式找出其余部分。所以如果我能找到符合这个模式的值,那应该就可以了。
编辑
针对 eyquem 的回复,我已经上传了我想要的内容前后的相关部分到 pastebin。我想提取 pageUrls
中的值。
2 个回答
这是一种不同的方法,给你提供一个替代的数据点。这里有一个用pyparsing来解决你问题的提取器,而不是使用正则表达式。你可能会发现这种方式在长期维护上更简单:
from pyparsing import Literal, quotedString, removeQuotes, delimitedList
# automatically strip quotes from quoted strings
# quotedString matches single or double quotes
quotedString.setParseAction(removeQuotes)
# define a pattern to extract the pageUrls: entry
pageUrlsSpec = Literal('pageUrls:') + '[' + delimitedList(quotedString)('urls') + ']'
for pageUrls in pageUrlsSpec.searchString(ss):
for url in pageUrls.urls:
print url
输出结果:
/blog/35318264c9a98faf79965c270ac80c5606774df1/data.pdf
/blog/da6645f6e22bf5f75974dc7eed5fcd6160d6b51e/data.pdf
/blog/6f90f101115140727c43cadee0b9e17881403a63/data.pdf
/blog/333584fc2850d1a1f97a0a7bf8c5a12e684856bf/data.pdf
/blog/9a018ecc48a37a9247a6404fd83e085384b445aa/data.pdf
请执行以下代码并把结果发给我。我会写一个正则表达式来获取你想要的数据。
注意:如果你把结果发到我的邮箱会更简单,这样也不会占用Stack Overflow的存储空间。
sock = urllib2.urlopen(request)
ch = sock.read()
sock.close()
print '\n'.join(str(i) + ' ' + repr(line)
for i,line in enumerate(ch.splitlines(True)))
正则表达式的速度至少比使用BeautifulSoup分析文本快20倍。
我说的是“分析”而不是“解析”
(对于那些认为HTML文本不能用正则表达式分析的人,我想说:ùù&ùè-_, sp*µùy43é' ##{[|:ù %yy~é"&'[[é(+F+"§.N/.M%%iyuo£$$ö!!!! sskrftttt §!!)
编辑 1
如果文本的组织方式像看起来那么规律,你甚至不需要正则表达式来分析它:
from io import StringIO
ss = '''<input type="hidden" name="__FOO" id="__FOO" value="garble" />
<script type="text/javascript">
//<![CDATA[
$(function () {
// convert to
var True = true, False = false;
manatee.start({
pageIDs: ["16798", "16799", "16800", "16801", "16802"],
userNames: ["Alice", "Bob", "Carol", "Dave", "Eve"],
wordCounts: [77,23,64,89,93],
linkCounts: [2,0,3,1,4],
pageUrls: ["","/blog/35318264c9a98faf79965c270ac80c5606774df1/data.pdf","/blog/da6645f6e22bf5f75974dc7eed5fcd6160d6b51e/data.pdf","/blog/6f90f101115140727c43cadee0b9e17881403a63/data.pdf","/blog/333584fc2850d1a1f97a0a7bf8c5a12e684856bf/data.pdf","/blog/9a018ecc48a37a9247a6404fd83e085384b445aa/data.pdf"],
toolbar: {
id: "ManateeToolbar",
buttons: [
{
id: "ManateeBrowser",
text: "Enter Fullscreen",
toggleText: "Escape Fullscreen"
}
]
}
});
});
//]]>
</script>
<script type="text/javascript">var blah</script>'''
simili_file = StringIO(ss)
for line in simili_file:
if line[0:13] == '\t\tpageUrls: [':
urls = tuple(el[1:-1] for el in line[13:line.find(']')].split(',') if el[1:-1])
print( urls )
结果
('/blog/35318264c9a98faf79965c270ac80c5606774df1/data.pdf',
'/blog/da6645f6e22bf5f75974dc7eed5fcd6160d6b51e/data.pdf',
'/blog/6f90f101115140727c43cadee0b9e17881403a63/data.pdf',
'/blog/333584fc2850d1a1f97a0a7bf8c5a12e684856bf/data.pdf',
'/blog/9a018ecc48a37a9247a6404fd83e085384b445aa/data.pdf')
编辑 2
为了让代码在文件内容变化时更安全,你也可以使用正则表达式:
ss = '''<input type="hidden" name="__FOO" id="__FOO" value="garble" />
<script type="text/javascript">
//<![CDATA[
$(function () {
// convert to
var True = true, False = false;
manatee.start({
pageIDs: ["16798", "16799", "16800", "16801", "16802"],
userNames: ["Alice", "Bob", "Carol", "Dave", "Eve"],
wordCounts: [77,23,64,89,93],
linkCounts: [2,0,3,1,4],
pageUrls: ["","/blog/35318264c9a98faf79965c270ac80c5606774df1/data.pdf","/blog/da6645f6e22bf5f75974dc7eed5fcd6160d6b51e/data.pdf","/blog/6f90f101115140727c43cadee0b9e17881403a63/data.pdf","/blog/333584fc2850d1a1f97a0a7bf8c5a12e684856bf/data.pdf","/blog/9a018ecc48a37a9247a6404fd83e085384b445aa/data.pdf"],
toolbar: {
id: "ManateeToolbar",
buttons: [
{
id: "ManateeBrowser",
text: "Enter Fullscreen",
toggleText: "Escape Fullscreen"
}
]
}
});
});
//]]>
</script>
<script type="text/javascript">var blah</script>'''
import re
regx = re.compile('^\t*pageUrls[\t ]*:[\t ]*\[(.*?)\],[\t ]*$',re.MULTILINE)
for mat in regx.finditer(ss):
urls = tuple(el[1:-1] for el in mat.group(1).split(',') if el[1:-1])
print( urls )
为了让这两段代码正常工作,网址中不能有','。
在第一段代码中,网址中也不能有']'。但我验证过:在Windows系统中,文件夹名称可以包含']'。
我在第二段代码中写的正则表达式是为了避免网址中出现','或']'的问题:正则表达式的结尾部分],[\t ]*$
要求']'字符后面只能跟空格或制表符,直到行尾。因为[\t ]
后面的星号'*'表示行尾可以有空格或制表符,但不是必须的。