1.代码功能概述
该代码使用 Playwright 异步库编写,用于抓取一个目标网站的数据。主要任务包括:
- 加载网页:访问指定页面并等待加载完成。
- 解析网页内容:提取数据如标题、封面图片、分类、评分、简介等。
- 存储数据:将抓取到的数据以特定格式保存到本地文件中。
2.代码结构解析
1. 导入模块
import asyncio
import logging
from os.path import exists
from playwright.async_api import async_playwright
from urllib.parse import urljoin
asyncio
:用于异步操作。logging
:用于记录程序的运行日志。os.path.exists
:检查指定路径是否存在。async_playwright
:Playwright 异步版本,用于浏览器自动化。urljoin
:用于拼接相对路径和基准路径,生成完整的 URL。
2. 全局变量与初始化
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
BASE_URL = 'https://ssr1.scrape.center'
TOTAL_PAGE = 2
RESULT_DIR = 'data'
exists(RESULT_DIR) or makedirs(RESULT_DIR)
- 设置日志记录格式和级别。
- 定义爬取的目标网站的基本 URL (
BASE_URL
) 和总页数 (TOTAL_PAGE
)。 RESULT_DIR
:存储数据的目录。- 如果结果存储目录不存在,则创建该目录。
3. 页面加载功能
async def scrape_page(page, url):
logging.info('scraping %s ...', url)
try:
await page.goto(url) # 跳转到目标 URL
await page.wait_for_load_state('networkidle') # 等待页面完全加载
except Exception as e:
logging.error(f"Error scraping {url}: {str(e)}", exc_info=True)
return False
return True
- 功能:访问指定的网页 URL,等待其加载完成。
- 异常处理:如果加载失败,记录错误并返回
False
。
4. 索引页和详情页抓取功能
async def scrape_index(page, page_index):
index_url = f'{BASE_URL}/page/{page_index}'
return await scrape_page(page, index_url)
async def scrape_detail(page, url):
return await scrape_page(page, url)
scrape_index
:访问分页列表页。scrape_detail
:访问详情页。
5. 索引页内容解析
async def parse_index(page):
elements = await page.query_selector_all('a.name') # 找到所有目标链接
for element in elements:
part_of_url = await element.get_attribute('href') # 获取链接地址
if part_of_url:
detail_url = urljoin(BASE_URL, part_of_url) # 拼接完整 URL
logging.info('get url: %s', detail_url)
yield detail_url
- 查找页面中所有指定的
<a>
标签,并提取其href
属性。 - 通过
urljoin
拼接为完整的详情页 URL。
6. 详情页内容解析
async def parse_detail(page):
name_tag = await page.query_selector('h2.m-b-sm')
name = await name_tag.text_content() if name_tag else None
cover_tag = await page.query_selector('img.cover')
cover = await cover_tag.get_attribute('src') if cover_tag else None
category_tags = await page.query_selector_all('div.categories > button > span')
categories = [await category.text_content() for category in category_tags] if category_tags else []
score_tag = await page.query_selector('p.score')
score = await score_tag.text_content().strip() if score_tag else None
drama_tag = await page.query_selector('div.drama > p')
drama = await drama_tag.text_content().strip() if drama_tag else None
return {
'name': name,
'cover': cover,
'categories': categories,
'drama': drama,
'score': score
}
- 使用 CSS 选择器提取以下内容:
- 标题:
h2.m-b-sm
。 - 封面图片链接:
img.cover
。 - 分类:
div.categories > button > span
。 - 评分:
p.score
。 - 简介:
div.drama > p
。
- 标题:
- 每个字段都有空值检查,避免因元素缺失导致错误。
7. 数据存储
async def save_data(data):
data_path = '{0}/movies_selector_async.txt'.format(RESULT_DIR)
with open(data_path, 'w', encoding='utf-8') as file:
name = data.get('name', None)
cover = data.get('cover', None)
categories = data.get('categories', None)
drama = data.get('drama', None)
score = data.get('score', None)
file.write('name:'+name+'\n')
file.write('cover:'+cover+'\n')
file.write('categories:'+str(categories)+'\n')
file.write('drama:'+drama+'\n')
file.write('score:'+score+'\n')
file.write('='*50 + '\n')
- 数据格式化后写入文件,采用
key:value
格式,并用分隔线分隔。
8. 主函数
async def main():
logging.info("开始采集")
playwright = await async_playwright().start()
browser = await playwright.chromium.launch(headless=True)
context = await browser.new_context()
page_1 = await context.new_page()
page_num = 3
flag = 0
for i in range(1, page_num):
url = "https://spa6.scrape.center/page/"+str(i)
await page_1.goto(url)
await page_1.wait_for_load_state('networkidle')
elements = await page_1.query_selector_all('div.el-card.item.m-t.is-hover-shadow')
page_2 = await context.new_page()
for element in elements:
logging.info("子页面采集开始")
tag_a = await element.query_selector('a')
detail_url = 'https://spa6.scrape.center' + await tag_a.get_attribute('href')
await scrape_detail(page_2, detail_url)
data = await parse_detail(page_2)
logging.info('get data: %s', data)
await save_data(data)
logging.info("子页面采集结束")
flag = flag+1
logging.info("全部数据采集结束,共:{}条".format(flag))
await browser.close()
- 打开浏览器并创建上下文与页面。
- 遍历分页,提取每页的所有详情链接并依次访问。
- 调用解析函数提取数据并保存。