使用BeautifulSoup </script>读取<script>的内容

时间:2011-11-05 14:33:58

标签: python regex beautifulsoup

我一直在努力阅读以下网站的来源:

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中的结果数可能会有所不同,令牌数也会有所不同,因此我无法使用位置索引来访问它。另外,我意识到BeautifulSoup是一个HTML解析器,并不解析JS。我如何使用正则表达式提取此信息?

如果没有简单的方法来获取所有,以下内容可能是妥协。 values的大多数的格式为"string1/xxxx/string2",其中xxxx是一些随机的SHA哈希值,每个哈希值都不同,我可以通过其他方式找出其余部分。所以,如果我只能找到符合这种模式的那些,那就应该没问题了。


修改

为了回应eyquem,我已经在我想要pastebin之前和之后上传了相关部分。我有兴趣检索pageUrls中的值。

2 个答案:

答案 0 :(得分:5)

请执行以下代码并发布结果。我将编写一个正则表达式来捕获您想要获取的数据。

Nota:如果你在我的电子邮件中发布它会更容易,它不会妨碍SO内存

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文本不能用正则表达式进行分析的人,我说:ùù&amp;ùè-_,sp *μùy43é'## {[|:ù%yy~é“&amp;'[[é(+ 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 ]之后的星号'*',只能在行尾添加制表符或空格,而不是强制性的。

答案 1 :(得分:2)

另一种方法是为您提供替代数据点。这是使用pyparsing而不是regex解决问题的提取器。从长远来看,您可能会发现这更容易维护:

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