【发布时间】:2020-11-07 21:52:54
【问题描述】:
我正在尝试抓取一个网站,但我在 Scrapy 的响应对象上使用的 Xpath 表达式有问题。
根据我对 XPath 的了解,我认为我使用了正确的 XPath 表达式。
所以我使用网络浏览器加载网页,然后下载并保存为 HTML 文件。
然后我尝试了两种不同的 XPath 表达式。
第一种方法是使用 Python 的 lxml.html 模块打开文件并将其作为 HTMLParser 对象加载。
第二种方法是使用 Scrapy 并将其指向保存的 HTML 文件。
在这两种情况下,我都使用了相同的 XPath 表达式。但我得到了不同的结果。
示例 HTML 代码是这样的(不完全是,但我不想逐字发布大量代码):
<html>
<body>
<div>
<table type="games">
<tbody>
<tr row="1">
<th data="week_number">1</th>
<td data="date">"9/13/2020"</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
例如,我试图在“TABLE”的“TR”元素下的“TH”元素中抓取星期数。
我使用 Chrome 而不是 Firefox 仔细检查了内容以检查文件(Firefox 将“tbody”元素添加到表中,根据这篇文章: Parsing HTML with XPath, Python and Scrapy
根据 Chrome 的检查,<tbody> 元素在文件中。
第一种方法是使用 lxml.html 模块打开 HTML 文件:
from lxml import etree, html
if __name__ == '__main__':
filename_04 = "/home/foo.html"
# Try opening the filename
try:
fh_04 = open(filename_04, "r")
except:
print "Error opening %s. Exiting" % filename_04
sys.exit(1)
# Try reading the contents of the HTML file.
# Then close the file
try:
content_04 = fh_04.read().decode('utf-8')
except UnicodeDecodeError:
print "Error trying to read as UTF-8. Exiting."
sys.exit(1)
fh_04.close()
# Define an HTML parser object
parser_04 = html.HTMLParser()
# Create a logical XML tree from the contents of parser_04
tree_04 = html.parse(StringIO(content_04), parser_04)
game_elements_list = list()
# Get all the <TR> elements from the <table type="games">
game_elements_list = tree_04.xpath("//table[@type = 'games']/tbody/tr")
num_games = len(game_elements_list)
# Now loop thru each of the <TR> element objects of game_elements_list
for x in range(num_games):
# Parse the week number using xpath()
# *** NOTE: this expression returns a list
parsed_week_number = game_elements_list[x].xpath(".//th[@data = 'week_number']/text()")
print ":: parsed_week_number: ", str(parsed_week_number)
p_type = type(parsed_week_number)
print ":: p_type: ", str(p_type)
通过 lxml.html 模块使用 XPath 表达式会返回以下输出:
:: parsed_week_number: ['1']
:: p_type: <type 'list'>
这是我对 XPath 表达式的期望,所以我的 XPath 表达式是正确的。
但是,当我将 Scrapy 蜘蛛指向本地文件时,我会得到不同的结果:
# I'm only posting the callback method, not the
# method that makes the actual request, because
# the request() call works
def parse_schedule_page(self, response):
game_elements_list = list()
# The xpath expression is the same as the one used in the file that
# uses lxml.html module
game_elements_list = response.xpath("//table[@type = 'games']/tbody/tr")
num_game_elements = len(game_elements_list)
for i in range(num_game_elements):
# Again, the XPath expression is the same
# as the one used in the file that
# uses the lxml.html module
parsed_week_number = game_elements_list[i].xpath(".//th[@data = 'week_number']/text()")
stmt = ":: parsed_week_number: " + str(parsed_week_number)
self.log(stmt)
p_type = type(parsed_week_number)
stmt = "p_type: " + str(p_type)
self.log(stmt)
"""
To get the week number, I have to add the following line:
week_number = parsed_week_number.extract()
"""
但在 Spider 的情况下,输出是不同的:
2020-07-17 21:22:30 [test_schedule] DEBUG: :: parsed_week_number: [<Selector xpath=".//th[@data-stat = 'week_num']/text()" data=u'1'>]
2020-07-17 21:22:30 [test_schedule] DEBUG: p_type: <class 'scrapy.selector.unified.SelectorList'>
相同的 XPath 表达式不返回 <th data="week_number">1</th> 的内容
我知道 Scrapy 使用与 lxml 的 HTMLParser 不同的提取器方法。但无论 HTML 数据如何存储,即使提取器方法不同,XPath 表达式的工作方式也不应该相同吗?
Scrapy 的 response.xpath() 方法对 XPath 表达式的评估与 lxml.html 的 xpath() 方法不同吗?
【问题讨论】: