底漆
使用爬虫爬行列表页面是网络数据提取中最常见的任务之一。 对于爬虫工程师来说,如何高效地生成提取规则是非常有必要的,否则大量的时间会浪费在编译爬虫CSS选择器或者XPath上。 本文将通过一个实际的反例来展示如何使用开源工具Webspot[1]手动提取列表页面。
网站点
Webspot 是一个致力于自动化 Web 数据提取的开源项目。 目前支持列表页和分页的识别以及抓取规则的提取。 此外css 分页,它还提供Web UI界面,允许用户直观地查看识别结果。 它还允许开发者使用API来获取识别结果。
Webspot的安装非常简单,可以参考官方文档[2]中的安装教程,使用Docker和Docker Compose进行安装。
# clone git repo
git clone https://github.com/crawlab-team/webspot
# start docker containers
docker-compose up -d
然后,等待程序启动,初始化应用程序大约需要半分钟。
初始化完成后,您可以访问Web界面:9999,您应该看到以下界面,这意味着它已经启动成功。
网站初始化接口
现在,您可以发起身份识别请求,点击新建请求,输入,点击提交,稍等片刻即可看到如下界面。
Webspot列表页面识别借助API自动抓取数据
接下来,我们将使用Python程序调用Webspot的API来手动抓取数据。
整个过程如下。
调用Webspot API获取提取规则(Extract Rules)css 分页,即列表页和分页的提取规则,提取规则就是CSS Selectors。
根据列表页的提取规则定义捕获目标,即列表页的每一项及其对应的数组。
根据分页提取规则确定抓取下一页的目标,让爬虫程序手动抓取下一页的数据。
调用API
调用API非常简单,只需将需要识别的URL传入body即可。 代码如下所示。
import requests
from bs4 import BeautifulSoup
from pprint import pprint
# API endpoint
api_endpoint = 'http://localhost:9999/api'
# url to extract
url = 'https://quotes.toscrape.com'
# call API to recognize list page and pagination elements
res = requests.post(f'{api_endpoint}/requests', json={
'url': 'https://quotes.toscrape.com'
})
results = res.json()
pprint(results)
使用Python Console运行它,您将得到识别结果数据,类似于下面的结果。
{...
'method': 'request',
'no_async': True,
'results': {'pagination': [{'detector': 'pagination',
'name': 'Next',
'score': 1.0,
'scores': {'score': 1.0},
'selectors': {'next': {'attribute': None,
'name': 'pagination',
'node_id': 120,
'selector': 'li.next > a',
'type': 'css'}}}],
...
'plain_list': [{...
'fields': [{'attribute': '',
'name': 'Field_text_1',
'node_id': None,
'selector': 'div.quote > span.text',
'type': 'text'},
...],
...}],
},
...}
识别结果包括列表页和分页的CSS选择器,以及列表页每一项对应的数组。
列表页和数组提取逻辑
接下来,我们将编译列表页和数组提取的逻辑。
首先,我们可以通过results获取列表页项目选择器list_items_selector和数组列表字段。
# list result
list_result = results['results']['plain_list'][0]
# list items selector
list_items_selector = list_result['selectors']['full_items']['selector']
print(list_items_selector)
# fields
fields = list_result['fields']
print(fields)
然后我们可以编写解析列表页面项的逻辑。
def get_data(soup: BeautifulSoup) -> list:
# data
data = []
# items
items_elements = soup.select(list_items_selector)
for el in items_elements:
# row data
row = {}
# iterate fields
for f in fields:
# field name
field_name = f['name']
# field element
field_element = el.select_one(f['selector'])
# skip if field element not found
if not field_element:
continue
# add field value to row
if f['type'] == 'text':
row[field_name] = field_element.text
else:
row[field_name] = field_element.attrs.get(f['attribute'])
# add row to data
data.append(row)
return data
里面代码的函数get_data中,我们传入了BeautifulSoup的实例,并使用list_items_selector和fields来解析并获取列表数据data,并返回给函数调用者。
请求列表页及分页逻辑
接下来我们需要编译请求列表页面和分页逻辑,即请求指定的URL,解析出分页,并调用里面的get_data。
我们首先需要获取用于分页的 CSS 选择器。
# pagination next selector
next_selector = results['results']['pagination'][0]['selectors']['next']['selector']
print(next_selector)
然后,我们编写爬虫逻辑,就是不断地爬取网站列表页面的数据。
def crawl(url: str) -> list:
# all data to crawl
all_data = []
while True:
print(f'requesting {url}')
# request url
res = requests.get(url)
# beautiful soup of html
soup = BeautifulSoup(res.content)
# add parsed data
data = get_data(soup)
all_data += data
# pagination next element
next_el = soup.select_one(next_selector)
# end if pagination next element not found
if not next_el:
break
# url of next page
url = urljoin(url, next_el.attrs.get('href'))
return all_data
这样我们就可以将所有的逻辑都编码化。
完整代码
下面是整个获取逻辑的完整代码。
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
from pprint import pprint
def get_data(soup: BeautifulSoup) -> list:
# data
data = []
# items
items_elements = soup.select(list_items_selector)
for el in items_elements:
# row data
row = {}
# iterate fields
for f in fields:
# field name
field_name = f['name']
# field element
field_element = el.select_one(f['selector'])
# skip if field element not found
if not field_element:
continue
# add field value to row
if f['type'] == 'text':
row[field_name] = field_element.text
else:
row[field_name] = field_element.attrs.get(f['attribute'])
# add row to data
data.append(row)
return data
def crawl(url: str) -> list:
# all data to crawl
all_data = []
while True:
print(f'requesting {url}')
# request url
res = requests.get(url)
# beautiful soup of html
soup = BeautifulSoup(res.content)
# add parsed data
data = get_data(soup)
all_data += data
# pagination next element
next_el = soup.select_one(next_selector)
# end if pagination next element not found
if not next_el:
break
# url of next page
url = urljoin(url, next_el.attrs.get('href'))
return all_data
if __name__ == '__main__':
# API endpoint
api_endpoint = 'http://localhost:9999/api'
# url to extract
url = 'https://quotes.toscrape.com'
# call API to recognize list page and pagination elements
res = requests.post(f'{api_endpoint}/requests', json={
'url': 'https://quotes.toscrape.com'
})
results = res.json()
pprint(results)
# list result
list_result = results['results']['plain_list'][0]
# list items selector
list_items_selector = list_result['selectors']['full_items']['selector']
print(list_items_selector)
# fields
fields = list_result['fields']
print(fields)
# pagination next selector
next_selector = results['results']['pagination'][0]['selectors']['next']['selector']
print(next_selector)
# start crawling
all_data = crawl(url)
# print crawled results
pprint(all_data[:50])
运行代码,最终可以得到如下结果数据。
[{'Field_link_url_6': '/author/Albert-Einstein',
'Field_link_url_8': '/tag/change/page/1/',
'Field_text_1': '“The world as we have created it is a process of our '
'thinking. It cannot be changed without changing our '
'thinking.”',
'Field_text_2': '“The world as we have created it is a process of our '
'thinking. It cannot be changed without changing our '
'thinking.”',
'Field_text_3': 'n'
' Tags:n'
' n'
'changen'
'deep-thoughtsn'
'thinkingn'
'worldn',
'Field_text_4': 'Albert Einstein',
'Field_text_5': '(about)',
'Field_text_7': 'change'},
...
这样,我们就借助Webspot实现了自动提取列表的爬虫任务。 无需显式定义CSS Selector或XPath,只需调用Webspot的API即可获取列表页面数据。
社区
如果您对作者的文章感兴趣,可以添加作者Momo tikazyq1并标记“码之道”,作者会将您拉入“码之道”交流群。
参考
[1]
网站:
[2]
官方文档:
CSS 如何进行投影
投影是指将物体或图像投影到二维平面上的过程,可以通过CSS实现。在 CSS 中,我们可以通过设置容器的样式和投影对象的样式来控制对象的投影效果。
下面我们将展示如何使用CSS来实现简单的投影效果。首先,我们需要创建一个容器并将其设置为无高度对象。之后,我们需要选择要投影的对象并将其设置为容器内间距为零的对象。最后,我们可以设置容器的样式来控制投影的功效。
示例代码:
/* 创建一个没有高度的对象 */
.容器{
宽度:300px;
高度:300px;
背景颜色:蓝色;
/* 创建一个内部间距为零的对象 */
.object{
宽度:300px;
高度:300px;
背景颜色:绿色;
位置:绝对;
顶部:0;
左:0;/*
设置容器的样式*/
.容器{
背景颜色:白色;
颜色:黑色;
填充:20px;
字体大小:24px;
在前面的代码中,我们创建了一个容器并将其设置为无高度对象。之后,我们创建了一个内部间距为零的对象css投影,并将其设置为容器内部间距为零的对象。最后,我们设置容器的样式来控制投影的效果。
通过设置容器的样式,我们可以控制投影对象的高度和长度。我们可以使用“top”、“left”和“right”属性来控制对象的位置。例如,如果我们将容器设置为“300px×300px”,则投影对象的高度为“300px”,长度为“300px”,并且将位于容器的中心。
不仅是容器的样式,还有投影对象的样式。例如,我们可以将投影对象设置为颜色或形状。通过设置投影对象的样式,我们可以更改对象的投影效果。
实际上,使用CSScss投影,我们可以使用简单的容器和投影对象来实现简单的投影效果。我们可以灵活地控制物体的位置、高度和长度,以及投影物体的颜色和形状。
发表评论