实时抓取网页数据(如何把网站提供的网页数据转换成格式化的数据(图))
优采云 发布时间: 2022-03-08 16:07实时抓取网页数据(如何把网站提供的网页数据转换成格式化的数据(图))
2022全球C++与系统软件技术大会| 3月11-12日上海点击查看详情>>>
背景网页抓取
大数据时代,一切都要用数据说话,而大数据处理的过程一般需要经过以下几个步骤
首先要做的是获取数据并提取有效数据,为下一步分析做准备。
有各种数据来源。我以为我是个足球爱好者,世界杯快到了,所以我想提取欧洲联赛的数据进行分析。很多网站都提供了详细的足球数据,比如:
这些网站提供了详细的足球数据,但是为了进一步分析,我们希望数据以格式化的形式存储,那么如何将这些网站提供的网页数据转换成格式化数据呢?? 这使用了网页抓取技术。简单来说,Web Scraping就是从网站中提取信息,通常使用程序来模拟人们浏览网页的过程,发送http请求,从http响应中获取结果。
网页抓取注意事项
在抓取数据之前,请注意以下几点:
Python Web Scraping 相关库
Python 提供了一个非常方便的 Web Scraping 基础,具有许多支持库。这是一个小选择
当然,您不必使用 Python,也不必编写自己的代码。建议关注import.io
网页抓取代码
接下来,我们将一步步使用Python从腾讯体育抓取2013/14赛季欧洲联赛的数据。
首先安装 Beautifulsoup
pip install beautifulsoup4
让我们从玩家的数据开始。
对玩家数据的网络请求是,返回的内容如下图所示:
web服务有两个参数,lega表示是哪个联赛,pn表示分页数。
首先,我们来做一些初始化准备
from urllib2 import urlopen
import urlparse
import bs4
BASE_URL = "http://soccerdata.sports.qq.com"
PLAYER_LIST_QUERY = "/playerSearch.aspx?lega=%s&pn=%d"
league = ['epl','seri','bund','liga','fran','scot','holl','belg']
page_number_limit = 100
player_fields = ['league_cn','img','name_cn','name','team','age','position_cn','nation','birth','query','id','teamid','league']
urlopen、urlparse、bs4 是我们将使用的 Python 库。
BASE_URL、PLAYER_LIST_QUERY、league、page_number_limit 和 player_fields 是我们将使用的一些常量。
下面是抓取玩家数据的具体代码:
def get_players(baseurl):
html = urlopen(baseurl).read()
soup = bs4.BeautifulSoup(html, "lxml")
players = [ dd for dd in soup.select('.searchResult tr') if dd.contents[1].name != 'th']
result = []
for player in players:
record = []
link = ''
query = []
for item in player.contents:
if type(item) is bs4.element.Tag:
if not item.string and item.img:
record.append(item.img['src'])
else :
record.append(item.string and item.string.strip() or 'na')
try:
o = urlparse.urlparse(item.a['href']).query
if len(link) == 0:
link = o
query = dict([(k,v[0]) for k,v in urlparse.parse_qs(o).items()])
except:
pass
if len(record) != 10:
for i in range(0, 10 - len(record)):
record.append('na')
record.append(unicode(link,'utf-8'))
record.append(unicode(query["id"],'utf-8'))
record.append(unicode(query["teamid"],'utf-8'))
record.append(unicode(query["lega"],'utf-8'))
result.append(record)
return result
result = []
for url in [ BASE_URL + PLAYER_LIST_QUERY % (l,n) for l in league for n in range(page_number_limit) ]:
result = result + get_players(url)
我们来看看抓取玩家数据的详细过程:
首先,我们定义一个 get_players 方法,该方法返回请求页面上所有玩家的数据。为了得到所有的数据,我们通过了一个for循环,因为我们要循环遍历每个联赛,每个联赛有多个页面。一般需要双循环:
for i in league:
for j in range(0, 100):
url = BASE_URL + PLAYER_LIST_QUERY % (l,n)
## send request to url and do scraping
Python 的列表推导可以通过构建列表轻松降低循环的级别。
此外,Python 有一个非常方便的合并连续列表的语法:list = list1 + list2
好,我们来看看如何使用BeautifulSoup来抓取网页中我们需要的内容。
首先调用urlopen读取对应url的内容,一般是html,用html构造一个beautifulsoup对象。
beautifulsoup 对象支持许多搜索功能,以及类似 css 的选择器。通常如果有一个 DOM 对象,我们使用下面的方式来查找它:
obj = soup.find("xx","cc")
另一种常用的方法是使用 CSS 选择器方法。在上面的代码中,我们选择了class=searchResult元素中的所有tr元素,过滤掉了th,也就是header元素。
for dd in soup.select('.searchResult tr') if dd.contents[1].name != 'th'
对于记录 tr 的每一行,都会生成一个玩家记录并将其存储在一个列表中。所以我们循环tr tr.contents 的内容来获取对应的字段内容。
对于每个tr的内容,我们首先检查它的类型是否为Tag。Tag 类型有几种情况。一种是收录 img 的案例。我们需要取出玩家头像图片的URL。
另一种是收录指向其他数据内容的链接
所以这些不同的情况在代码中分别处理。
对于一个Tag 对象,Tag.x 可以得到它的子对象,Tag['x'] 可以得到Tag 的属性值。
所以使用 item.img['src'] 来获取 item 的子元素 img 的 src 属性。
对于已经收录链接的情况,我们使用urlparse来获取查询url中的参数。在这里,我们利用字典理解将查询参数放入字典,然后添加到列表中。
dict([(k,v[0]) for k,v in urlparse.parse_qs(o).items()])
对于其他情况,我们使用 Python 的 and or 表达式来保证当 Tag 的内容为空时,我们写 'na',类似于三元运算符 X ? A : B 在 C/C++ 或 Java 中
然后有一段代码判断当前记录的长度是否大于10,如果不大于10则用空值填充,以避免出现一些不一致的情况。
if len(record) != 10:
for i in range(0, 10 - len(record)):
record.append('na')
最后,我们在查询中添加一些相关参数,比如球员的id、球队的id、联赛代码等到列表中。
record.append(unicode(link,'utf-8'))
record.append(unicode(query["id"],'utf-8'))
record.append(unicode(query["teamid"],'utf-8'))
record.append(unicode(query["lega"],'utf-8'))
最后,我们把这个页面上所有玩家的列表放到一个列表中,然后返回。
好的,现在我们有了一个收录所有玩家信息的列表,我们需要保存它以供进一步处理、分析。通常,csv 格式是一种常见的选择。
import csv
def write_csv(filename, content, header = None):
file = open(filename, "wb")
file.write('\xEF\xBB\xBF')
writer = csv.writer(file, delimiter=',')
if header:
writer.writerow(header)
for row in content:
encoderow = [dd.encode('utf8') for dd in row]
writer.writerow(encoderow)
write_csv('players.csv',result,player_fields)
这里需要注意的是编码的问题。因为我们使用的是utf-8编码方式,所以在csv的文件头中,我们需要写\xEF\xBB\xBF,详见这篇文章文章
好了,现在大功告成,捕获的csv如下:
因为之前我们也抓取了本赛季球员的比赛详情,所以我们可以进一步抓取所有球员每场比赛的记录
抓取的代码如下
def get_player_match(url):
html = urlopen(url).read()
soup = bs4.BeautifulSoup(html, "lxml")
matches = [ dd for dd in soup.select('.shtdm tr') if dd.contents[1].name != 'th']
records = []
for item in [ dd for dd in matches if len(dd.contents) > 11]: ## filter out the personal part
record = []
for match in [ dd for dd in item.contents if type(dd) is bs4.element.Tag]:
if match.string:
record.append(match.string)
else:
for d in [ dd for dd in match.contents if type(dd) is bs4.element.Tag]:
query = dict([(k,v[0]) for k,v in urlparse.parse_qs(d['href']).items()])
record.append('teamid' in query and query['teamid'] or query['id'])
record.append(d.string and d.string or 'na')
records.append(record)
return records[1:] ##remove the first record as the header
def get_players_match(playerlist, baseurl = BASE_URL + '/player.aspx?'):
result = []
for item in playerlist:
url = baseurl + item[10]
print url
result = result + get_player_match(url)
return result
match_fields = ['date_cn','homeid','homename_cn','matchid','score','awayid','awayname_cn','league_cn','firstteam','playtime','goal','assist','shoot','run','corner','offside','foul','violation','yellowcard','redcard','save']
write_csv('m.csv',get_players_match(result),match_fields)
爬取过程与之前类似。
接下来做什么
既然我们已经有了详细的欧联杯数据,接下来我们应该怎么做呢?我建议您将数据导入 BI 工具以进行进一步分析。有两个更好的选择:
Tableau 在数据可视化领域是无与伦比的。Tableau Public 完全免费。它使用数据可视化来驱动数据探索和分析,具有非常好的用户体验。
Splunk 提供了一个大数据平台,主要针对机器数据。支持每天免费导入500M数据,个人学习应该够用了。
当然你也可以使用 Excel。另外,如果大家有什么好的免费数据分析平台,欢迎交流。