查看页面源码:
<td class="cntBoxGreyLnk" rowspan="2" valign="top">
<script type="text/javascript" src="http://www.oil-price.net/COMMODITIES/gen.php?lang=en"></script>
<noscript> To get live <a href="http://www.oil-price.net/dashboard.php?lang=en#COMMODITIES">gold, oil and commodity price</a>, please enable Javascript.</noscript>
你想要的数据被动态加载到页面中;您无法使用 BeautifulSoup 获得它,因为它在 HTML 中不存在。
如果您查看链接的脚本网址http://www.oil-price.net/COMMODITIES/gen.php?lang=en
你会看到一堆类似的 javascript
document.writeln('<table summary=\"Crude oil and commodity prices (c) http://oil-price.net\" style=\"font-family: Lucida Sans Unicode, Lucida Grande, Sans-Serif; font-size: 12px; background: #fff; border-collapse: collapse; text-align: left; border-color: #6678b1; border-width: 1px 1px 1px 1px; border-style: solid;\">');
document.writeln('<thead>');
/* ... */
document.writeln('<tr>');
document.writeln('<td style=\"font-size: 12px; font-weight: bold; border-bottom: 1px solid #ccc; color: #1869bd; padding: 2px 6px; white-space: nowrap;\">');
document.writeln('<a href=\"http://oil-price.net/dashboard.php?lang=en#COMMODITIES\" style=\"color: #1869bd; text-decoration:none\">Heating Oil<\/a>');
document.writeln('<\/td>');
document.writeln('<td style=\"font-size: 12px; font-weight: normal; border-bottom: 1px solid #ccc; color: #000000; padding: 2px 6px; white-space: nowrap;\">');
document.writeln('3.05');
document.writeln('<\/td>');
document.writeln('<td style=\"font-size: 12px; font-weight: normal; border-bottom: 1px solid #ccc; color: green; padding: 2px 6px; white-space: nowrap;\">');
document.writeln('+1.81%');
document.writeln('<\/td><\/tr>');
当页面加载时,此 javascript 会运行并动态写入您要查找的值。 (顺便说一句:这是一种完全过时、被贬低且普遍可怕的做事方式;我只能假设有人认为这是额外的安全保障。他们应该因为他们的冒失而受到惩罚! em>)。
现在,这段代码非常简单;您可能可以使用正则表达式获取 html 数据。但是 (a) 有一些转义码可能会导致问题,(b) 不能保证他们将来不会混淆他们的代码,以及 (c) 那里的乐趣在哪里?
PyV8 module 提供了一种直接从 Python 执行 javascript 代码的方法,甚至允许我们编写可调用 javascript 的 Python 代码!我们将利用这一点以不可混淆的方式获取数据:
import PyV8
import requests
from bs4 import BeautifulSoup
SCRIPT = "http://www.oil-price.net/COMMODITIES/gen.php?lang=en"
class Document:
def __init__(self):
self.lines = []
def writeln(self, s):
self.lines.append(s)
@property
def content(self):
return '\n'.join(self.lines)
class DOM(PyV8.JSClass):
def __init__(self):
self.document = Document()
def main():
# Create a javascript context which contains
# a document object having a writeln method.
# This allows us to capture the calls to document.writeln()
dom = DOM()
ctxt = PyV8.JSContext(dom)
ctxt.enter()
# Grab the javascript and execute it
js = requests.get(SCRIPT).content
ctxt.eval(js)
# The result is the HTML code you are looking for
html = dom.document.content
# html is now "<table> ... </table>" containing the data you are after;
# you can go ahead and finish parsing it with BeautifulSoup
tbl = BeautifulSoup(html)
for row in tbl.findAll('tr'):
print(' / '.join(td.text.strip() for td in row.findAll('td')))
if __name__ == "__main__":
main()
这会导致:
Crude Oil / 99.88 / +2.04%
Natural Gas / 4.78 / -3.27%
Gasoline / 2.75 / +2.40%
Heating Oil / 3.05 / +1.81%
Gold / 1263.30 / +0.45%
Silver / 19.92 / +0.06%
Copper / 3.27 / +0.37%
这是你想要的数据。
编辑:我真的不能再把它弄糊涂了;这是完成这项工作的最低限度的代码。但也许我可以更好地解释它是如何工作的(它真的没有看起来那么可怕!):
PyV8 模块以 Python 可以与之交互的方式包装了 Google 的 V8 javascript 解释器。您需要先到https://code.google.com/p/pyv8/downloads/list 下载并安装相应的版本,然后才能运行我的代码。
javascript 语言本身并不知道如何与外界交互;它没有内置的输入或输出方法。这不是非常有用。为了解决这个问题,我们可以传入一个“上下文对象”,其中包含有关外部世界以及如何与之交互的信息。当 javascript 在网络浏览器中运行时,它会获取一个上下文对象,该对象提供有关浏览器和当前网页以及如何与它们交互的各种信息。
http://www.oil-price.net/COMMODITIES/gen.php?lang=en 的 javascript 代码假定它将在浏览器中运行,其中上下文有一个表示网页的“文档”对象,该对象有一个“writeln”方法,可以将文本附加到当前的末尾网页。随着页面的加载,脚本被加载并运行;它将文本(恰好是有效的 HTML)写入页面;这将作为页面的一部分呈现,最终成为您想要的 Commodities 表。您无法使用 BeautifulSoup 获取表,因为该表在 javascript 运行之前不存在,并且 BeautifulSoup 不会加载或运行 javascript。
我们要运行 javascript;为此,我们需要一个假的浏览器上下文,它有一个带有“writeln”方法的“document”对象。然后我们需要存储传递给“writeln”的信息,并且我们需要一种在脚本完成时将其取回的方法。我的 DOM 类是假的浏览器上下文;当实例化时(即当我们创建其中一个时),它给自己一个名为 document 的 Document 对象,它有一个 writeln 方法。当 document.writeln 被调用时,它会将文本行追加到 document.lines 中,并且我们可以随时调用 document.content 来取回到目前为止写入的所有文本。
现在:行动!在 main 函数中,我们创建了一个假的浏览器上下文,将其设置为解释器的当前上下文,然后启动解释器。我们抓取 javascript 代码,并告诉解释器评估(即运行)它。 (源代码混淆,会搞砸静态分析,不会影响我们,因为代码在运行时必须产生良好的输出,而我们实际上是在运行它!)代码完成后,我们从文档中获取最终输出。语境;这是您无法获得的表格 html。我们将其传回 BeautifulSoup 以提取数据,然后打印数据。
希望有帮助!