如何用Python和Scrapy将网络变成数据
自成立以来,网站就被用来分享信息。无论是维基百科的文章、YouTube频道、Instagram账户,还是Twitter手柄。它们都充满了有趣的数据,每个人只要能上网和使用网络浏览器,就可以获得。
但是,如果我们想以编程方式获得任何特定的数据呢?
有两种方法可以做到这一点:
- 使用官方API
- 网络刮削
API(应用编程接口)的概念是为了在不同的系统之间以标准的方式交换数据。但是,大多数时候,网站所有者并不提供任何API。在这种情况下,我们只剩下使用网络刮削来提取数据的可能性。
基本上,每个网页都是以HTML格式从服务器返回的。这意味着,我们的实际数据被很好地包装在HTML元素中。这使得检索特定数据的整个过程变得非常简单和直接。
本教程将是你学习使用Python编程语言进行网络刮削的终极指南。首先,我将通过一些基本的例子让你熟悉网络抓取。稍后,我们将利用这些知识从Livescore.cz提取足球比赛的数据。
不多说了,让我们跟着我一起去吧。
开始吧
为了让我们开始,你需要启动一个新的Python3项目,并安装 Scrapy (一个Python的网络刮擦和网络爬行库)。我在本教程中使用pipenv,但你可以使用 pip 和 venv,或者 conda。
pipenv install scrapy
在这一点上,你有了Scrapy,但你仍然需要创建一个新的网络抓取项目,为此,Scrapy为我们提供了一个命令行,为我们做这些工作。
现在让我们通过使用scrapy cli创建一个名为web_scraper 的新项目。
如果你像我一样使用pipenv ,请使用。
pipenv run scrapy startproject web_scraper .
否则,从你的虚拟环境中使用
scrapy startproject web_scraper .
这将在当前目录下创建一个基本项目,结构如下。
scrapy.cfg
web_scraper/
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
spiders/
__init__.py
用XPath查询建立我们的第一个蜘蛛
我们将以一个非常简单的例子开始我们的网络刮擦教程。首先,我们将在HTML中找到Live Code Stream网站的标志。我们知道它只是一个文本,而不是一个图像,所以我们将简单地提取这个文本。
代码
为了开始工作,我们需要为这个项目创建一个新的spider。我们可以通过创建一个新的文件或使用CLI来实现。
由于我们已经知道我们需要的代码,我们将在这个路径上创建一个新的Python文件 /web_scraper/spiders/live_code_stream.py
下面是这个文件的内容。
import scrapy
class LiveCodeStreamSpider(scrapy.Spider):
name = "lcs"
start_urls = [
"https://livecodestream.dev/"
]
def parse(self, response):
yield {
'logo': response.xpath("/html/body/header/nav/a[1]/text()").get()
}
代码解释
-
首先,我们导入了Scrapy库。这是因为我们需要它的功能来创建一个Python网络蜘蛛。然后这个蜘蛛将被用来抓取指定的网站并从中提取有用的信息。
-
我们创建了一个类,并将其命名为
LiveCodeStreamSpider。基本上,它继承了scrapy.Spider,这就是为什么我们把它作为一个参数传递。 -
现在,一个重要的步骤是用一个叫做
name的变量为你的蜘蛛定义一个独特的名字。请记住,你不允许使用现有蜘蛛的名字。同样地,你也不能用这个名字来创建新的蜘蛛。它在整个项目中必须是唯一的。 -
之后,我们使用
start_urls列表传递网站的URL。 -
最后,创建一个名为
parse()的方法,该方法将在HTML代码内定位标志并提取其文本。在Scrapy中,有两种方法可以在源代码里面找到HTML元素。下面提到了这些方法。- CSS
- XPath
你甚至可以使用一些外部库,如BeautifulSoup和lxml。但是,在这个例子中,我们使用了XPath。
确定任何HTML元素的XPath的一个快速方法是在Chrome DevTools中打开它。现在,只需在该元素的HTML代码上点击右键,将鼠标指针悬停在刚刚出现的弹出菜单内的 "复制 "上。最后,点击 "复制XPath "菜单项。
顺便说一下,我在元素的实际XPath后面使用了
/text(),只检索该元素的文本,而不是完整的元素代码。
注意: 你不允许为上面提到的变量、列表或函数使用任何其他名称。这些名字是在Scrapy库中预先定义的。所以,你必须按原样使用它们。否则,该程序将不能按预期工作。
运行 "蜘蛛"
由于我们已经在命令提示符中的web_scraper文件夹内。让我们执行我们的蜘蛛,并使用下面的代码将结果填入一个新文件lcs.json中。是的,我们得到的结果将是使用JSON格式的良好结构。
pipenv run scrapy crawl lcs -o lcs.json
scrapy crawl lcs -o lcs.json
结果
当上述代码执行后,我们会在项目文件夹中看到一个新文件lcs.json。
下面是这个文件的内容。
[
{"logo": "Live Code Stream"}
]
另一个使用CSS查询选择器的蜘蛛
我们大多数人都喜欢运动,而说到足球,它是我个人的最爱。
世界各地经常举办足球比赛。有几个网站在比赛进行时提供比赛结果的直播。但是,这些网站大多不提供任何官方API。
反过来,这也为我们创造了一个机会,可以利用我们的网络刮擦技能,通过直接刮擦他们的网站来提取有意义的信息。
例如,让我们看一下Livescore.cz网站。
在他们的主页上,他们很好地展示了今天(你访问该网站的日期)将进行的锦标赛和比赛。
我们可以检索到这样的信息
- 锦标赛名称
- 比赛时间
- 球队1名称(如国家、足球俱乐部等)。
- 球队1的目标
- 球队2名称(如国家、足球俱乐部等)。
- 2队进球
- 等等。
在我们的代码示例中,我们将提取今天有比赛的锦标赛名称。
代码
让我们在我们的项目中创建一个新的spider来检索锦标赛的名称。我将这个文件命名为livescore_t.py
以下是你需要在 /web_scraper/web_scraper/spiders/livescore_t.py中输入的代码
import scrapy
class LiveScoreT(scrapy.Spider):
name = "LiveScoreT"
start_urls = [
"https://livescore.cz/"
]
def parse(self, response):
for ls in response.css('#soccer_livescore .tournament'):
yield {
'tournament': ls.css('.nation a::text').get()
}
代码解释
-
像往常一样,导入Scrapy。
-
创建一个继承了scrapy.Spider属性和功能的类。
-
给我们的蜘蛛起一个独特的名字。在这里,我使用了
LiveScoreT,因为我们将只提取锦标赛的名称。 -
下一步是提供Livescore.cz的URL。
-
最后,
parse()函数循环处理所有包含赛事名称的匹配元素,并使用yield将其连接起来。最后,我们收到所有今天有匹配的锦标赛名称。需要注意的是,这次我使用了CSS选择器而不是XPath。
运行新创建的spider
现在是时候看看我们的蜘蛛的行动了。运行下面的命令,让蜘蛛抓取Livescore.cz网站的主页。网络抓取的结果将被添加到一个JSON格式的名为ls_t.json的新文件中。
pipenv run scrapy crawl LiveScoreT -o ls_t.json
现在你已经知道该怎么做了。
结果
这是我们的网络蜘蛛在2020年11月18日从Livescore.cz提取的内容。记住,输出结果可能每天都在变化。
[
{"tournament": "International - World Cup Qualification CONMEBOL"},
{"tournament": "Brazil - Serie A"},
{"tournament": "International - UEFA Nations League A Grp. 3"},
{"tournament": "International - UEFA Nations League A Grp. 4"},
{"tournament": "International - UEFA Nations League C Grp. 1"},
{"tournament": "International - UEFA Nations League D Grp. 1"},
{"tournament": "International - UEFA Nations League D Grp. 2"},
{"tournament": "..."}
]
一个更高级的用例
在这一节中,我们将不再仅仅检索比赛的名称。我们将更进一步,获得完整的锦标赛和他们的比赛的细节。
在**/web_scraper/web_scraper/spiders/中创建一个新文件,并将其命名为livescore.py**。现在,在其中输入以下代码。
import scrapy
class LiveScore(scrapy.Spider):
name = "LiveScore"
start_urls = [
"https://www.livescore.cz/yesterday.php"
]
def parse(self, response):
table_tr = response.css('tr')
tournaments = []
for tr in table_tr:
if tr.css('.tournament'):
tournaments.append({
'name': tr.css('.nation a::text').get(),
'matches': []
})
elif tr.css('.match'):
team_score = tr.css('.col-score strong::text').get()
if team_score is not None:
team_1_score = team_score.split(':')[0]
team_2_score = team_score.split(':')[1]
else:
team_1_score = None
team_2_score = None
tournaments[-1]['matches'].append({
'time': tr.css('.match .col-time time::attr(datetime)').get(),
'state': tr.css('.match .col-state span::text').get(),
'team_1_name': tr.css('.col-home a::text').get(),
'team_1_score': team_1_score,
'team_2_name': tr.css('.col-guest a::text').get(),
'team_2_score': team_2_score
})
for t in tournaments:
yield {
'tournament': t
}
代码解释
这个文件的代码结构与我们之前的例子相同。在这里,我们只是用新的功能更新了parse() 方法。
基本上,我们从页面中提取了所有的HTML<tr></tr> 元素。然后,我们对它们进行循环,找出它是一个锦标赛还是一场比赛。如果它是一个锦标赛,我们提取它的名字。如果是比赛,我们提取了它的 "时间"、"状态 "和 "两队的名称和得分"。
运行这个例子
在控制台中输入以下命令并执行。
pipenv run scrapy crawl LiveScore -o ls.json
结果
下面是它检索到的一个样本。
[{
"tournament": {
"name": "International - World Cup Qualification CONMEBOL",
"matches": [{
"time": "2020-11-18T00:00:00+01:00",
"state": null,
"team_1_name": "Uruguay",
"team_1_score": "0",
"team_2_name": "Brazil",
"team_2_score": "2"
}, {
"time": "2020-11-18T00:00:00+01:00",
"state": null,
"team_1_name": "Paraguay",
"team_1_score": "2",
"team_2_name": "Bolivia",
"team_2_score": "2"
}, {
"time": "2020-11-18T01:30:00+01:00",
"state": null,
"team_1_name": "Peru",
"team_1_score": "0",
"team_2_name": "Argentina",
"team_2_score": "2"
}]
}
}]
现在有了这些数据,我们可以做任何我们想做的事情,比如用它来训练我们自己的神经网络来预测未来的比赛:p。
结论
数据分析师经常使用网络刮削,因为它有助于他们收集数据以预测未来。同样,企业也使用它从网页中提取电子邮件,因为它是一种有效的线索生成方式。我们甚至可以用它来监测产品的价格。
换句话说,网络刮削有很多用例,而Python完全有能力做到这一点。
那么,你还在等什么呢?现在就试试搜刮你喜欢的网站吧。
谢谢你的阅读!