I'm using lxml to scrape some HTML that l开发者_C百科ooks like this:
<div align=center><a style="font-size: 1.1em">Football</a></div>
<a href="">Team A</a>
<a href="">Team B</a>
<div align=center><a style="font-size: 1.1em">Baseball</a></div>
<a href="">Team C</a>
<a href="">Team D</a>
How can I end up with data in the form
[ {'category': 'Football', 'title': 'Team A'},
{'category': 'Football', 'title': 'Team B'},
{'category': 'Baseball', 'title': 'Team C'},
{'category': 'Baseball', 'title': 'Team D'}]
So far I've got:
results = []
for (i,a) in enumerate(content[0].xpath('./a')):
data['text'] = a.text
results.append(data)
But I don't know how to get the category name by splitting at font-size
and retaining sibling tags - any advice?
Thanks!
I had success with the following code:
#!/usr/bin/env python
snippet = """
<html><head></head><body>
<div align=center><a style="font-size: 1.1em">Football</a></div>
<a href="">Team A</a>
<a href="">Team B</a>
<div align=center><a style="font-size: 1.1em">Baseball</a></div>
<a href="">Team C</a>
<a href="">Team D</a>
</body></html>
"""
import lxml.html
html = lxml.html.fromstring(snippet)
body = html[1]
results = []
current_category = None
for element in body.xpath('./*'):
if element.tag == 'div':
current_category = element.xpath('./a')[0].text
elif element.tag == 'a':
results.append({ 'category' : current_category,
'title' : element.text })
print results
It will print:
[{'category': 'Football', 'title': 'Team A'},
{'category': 'Football', 'title': 'Team B'},
{'category': 'Baseball', 'title': 'Team C'},
{'category': 'Baseball', 'title': 'Team D'}]
Scraping is fragile. Here for example, we depend explicitly on the ordering of the elements as well as the nesting. However, sometimes such a hardwired approach might be good enough.
Here is another (more xpath-oriented approach) using the preceding-sibling
axis:
#!/usr/bin/env python
snippet = """
<html><head></head><body>
<div align=center><a style="font-size: 1.1em">Football</a></div>
<a href="">Team A</a>
<a href="">Team B</a>
<div align=center><a style="font-size: 1.1em">Baseball</a></div>
<a href="">Team C</a>
<a href="">Team D</a>
</body></html>
"""
import lxml.html
html = lxml.html.fromstring(snippet)
body = html[1]
results = []
for e in body.xpath('./a'):
results.append(dict(
category=e.xpath('preceding-sibling::div/a')[-1].text,
title=e.text))
print results
Also if you are looking for other ways(just an option - don't beat me too much) how to do this or you don't have ability to import lxml you can use the following weird code:
text = """
<a href="">Team YYY</a>
<div align=center><a style="font-size: 1.1em">Polo</a></div>
<div align=center><a style="font-size: 1.1em">Football</a></div>
<a href="">Team A</a>
<a href="">Team B</a>
<div align=center><a style="font-size: 1.1em">Baseball</a></div>
<a href="">Team C</a>
<a href="">Team D</a>
<a href="">Team X</a>
<div align=center><a style="font-size: 1.1em">Tennis</a></div>
"""
# next variables could be modified depending on what you really need
keyStartsWith = '<div align=center><a style="font-size: 1.1em">'
categoryStart = len(keyStartsWith)
categoryEnd = -len('</a></div>')
output = []
data = text.split('\n')
titleStart = len('<a href="">')
titleEnd = -len('</a>')
getdict = lambda category, title: {'category': category, 'title': title}
# main loop
for i, line in enumerate(data):
line = line.strip()
if keyStartsWith in line and len(data)-1 >= i+1:
category = line[categoryStart: categoryEnd]
(len(data)-1 == i and output.append(getdict(category, '')))
if i+1 < len(data)-1 and keyStartsWith in data[i+1]:
output.append(getdict(category, ''))
else:
while i+1 < len(data)-1 and keyStartsWith not in data[i+1]:
title = data[i+1].strip()[titleStart: titleEnd]
output.append(getdict(category, title))
i += 1
精彩评论