我正试图通过here来消除赔率。
目前只是尝试使用以下蜘蛛记录结果:
def parse(self, response):
log.start("LogFile.txt", log.DEBUG);
hxs = HtmlXPathSelector(response)
sites = hxs.select('//div[@class="fb_day_type_wrapper"]')
items = []
for site in sites:
siteAddress = urlparse.urljoin(response.url, site.extract())
self.log('Found category url: %s' % siteAddress)
这仅记录条目:此市场目前无法使用.... 不是包含赔率的其他元素。
我尝试了几个没有运气的不同选择器。看起来,一旦我尝试进入元素div[@class="fb_day_type_wrapper"]
,我什么也得不到。使用scrapy shell我有相同的结果。
答案 0 :(得分:6)
该网站使用javascript生成数据表。有一些替代方法,如scrapyjs或splash,可以获取js呈现的html页面。如果您只需要刮一页,最好使用Selenium。
否则,您可能需要进入硬核模式并使用数据对站点中发生的情况进行逆向工程。我会告诉你如何做到这一点。
首先,启动scrapy shell
,以便我们可以浏览网页:
scrapy shell http://www.paddypower.com/football/football-matches/premier-league
注意:我使用的是python 2.7.4,ipython 0.13.2和scrapy 0.18.0。
如果您在浏览器中查找“Crystal Palace v Fulham”的源代码,您会看到有一个具有该引用的javascript代码。 <script>
块看起来像:
document.bodyOnLoad.push(function() {
lb_fb_cpn_init(
"",
"html",
"MR_224",
{category: 'SOCCER',
我们在shell中查找此元素:
In [1]: hxs.select('//script[contains(., "lb_fb_cpn_init")]')
Out[1]: [<HtmlXPathSelector xpath='//script[contains(., "lb_fb_cpn_init")]' data=u'<script type="text/javascript">\n/* $Id: '>]
如果查询lb_fb_cpn_init
参数,您将看到我们要查找的数据以此形式作为参数传递:
[{names: {en: 'Newcastle v Liverpool'}, ...
事实上有三个这样的论点:
In [2]: hxs.select('//script[contains(., "lb_fb_cpn_init")]').re('\[{names:')
Out[2]: [u'[{names:', u'[{names:', u'[{names:']
所以我们提取所有这些,注意我们使用了很多正则表达式:
In [3]: js_args = hxs.select('//script[contains(., "lb_fb_cpn_init")]').re(r'(\[{names:(?:.+?)\]),')
In [4]: len(js_args)
Out[4]: 3
这里的想法是我们想要将javascript代码(这是一个文字对象)解析为python代码(一个字典)。我们可以使用json.loads
但是为了这样做,js代码必须是一个有效的json对象,即在""
中包含字段名和字符串。
我们继续这样做。首先,我将单个字符串中的参数作为javascript列表加入:
In [5]: args_raw = '[{}]'.format(', '.join(js_args))
然后我们将字段名称括在""
和中,用双引号替换单引号:
In [6]: import re
In [7]: args_json = re.sub(r'(,\s?|{)(\w+):', r'\1"\2":', args_raw).replace("'", '"')
这可能并不总是适用于所有情况,因为javascript代码的模式可能不容易被单个re.sub
和/或.replace
替换。
我们准备将javascript代码解析为json对象:
In [8]: import json
In [9]: data = json.loads(args_json)
In [10]: len(data)
Out[10]: 3
在这里,我只是在寻找活动名称和赔率。您可以查看data
内容,了解它的外观。
幸运的是,数据似乎有相关性:
In [11]: map(len, data)
Out[11]: [20, 20, 60]
您也可以使用dict
字段从其中的三个ev_id
构建一个data[0]
。我将假设data[1]
和data[2]
具有直接关联,In [12]: map(lambda v: v['ev_id'], data[2])
Out [12]:
[5889932,
5889932,
5889932,
5889933,
5889933,
5889933,
...
每个事件包含3个项目。这可以通过以下方式轻松验证:
In [13]: odds = iter(data[2])
In [14]: odds_merged = zip(odds, odds, odds)
In [15]: data_merged = zip(data[0], data[1], odds_merged)
In [16]: len(data_merged)
Out[16]: 20
使用一些python-fu,我们可以合并记录:
In [17]: get_odd = lambda obj: (obj['names']['en'], '/'.join([obj['lp_num'], obj['lp_den']]))
In [18]: event_odds = []
In [19]: for event, _, odds in data_merged:
....: event_odds.append({'name': event['names']['en'], 'odds': dict(map(get_odd, odds)), 'url': event['url']})
....:
In [20]: event_odds
Out[20]:
[{'name': u'Newcastle v Liverpool',
'odds': {u'Draw': u'14/5', u'Liverpool': u'17/20', u'Newcastle': u'3/1'},
'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Newcastle%2dv%2dLiverpool-5889932.html'},
{'name': u'Arsenal v Norwich',
'odds': {u'Arsenal': u'3/10', u'Draw': u'9/2', u'Norwich': u'9/1'},
'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Arsenal%2dv%2dNorwich-5889933.html'},
{'name': u'Chelsea v Cardiff',
'odds': {u'Cardiff': u'10/1', u'Chelsea': u'1/4', u'Draw': u'5/1'},
'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Chelsea%2dv%2dCardiff-5889934.html'},
{'name': u'Everton v Hull',
'odds': {u'Draw': u'10/3', u'Everton': u'4/9', u'Hull': u'13/2'},
'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Everton%2dv%2dHull-5889935.html'},
{'name': u'Man Utd v Southampton',
'odds': {u'Draw': u'3/1', u'Man Utd': u'8/15', u'Southampton': u'11/2'},
'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Man%2dUtd%2dv%2dSouthampton-5889939.html'},
...
最后,我们收集数据:
log.start
正如您所看到的,网页抓取可能非常具有挑战性(而且很有趣!)。这完全取决于网站如何显示数据。在这里你可以通过使用Selenium来节省时间,但如果你想要刮掉一个大型网站,与Scrapy相比,Selenium会非常慢。
此外,您还必须考虑该网站是否会经常获得代码更新,在这种情况下,您将花费更多时间对js代码进行逆向工程。在这种情况下,scrapyjs或splash等解决方案可能是更好的选择。
最后的评论:
LOG_FILE
。使用设置--set LOG_FILE=mylog.txt
(命令行参数:.extract()
)。