1. 定义
Scrapy是一个Python编写的开源网络爬虫框架,用于快速、高效地从网站中提取结构化的数据。它提供了一个完整的工具集,使得开发者可以轻松地构建和部署爬虫程序。
2. 组件
- 引擎(Engine):用来控制整个系统的数据处理流程
-
Spiders(爬虫):定义了如何提取网站数据的规则,以及如何跟踪链接和处理页面。Spiders定义了起始URL、从页面中提取数据的方式以及如何跟踪链接等。
-
Items(数据项):用于定义要抓取的数据结构,包含你所关心的数据字段。可以将网页中抓取的数据封装到Items对象中,方便进一步处理和存储。每个Item类似于一个字典,用于存储和提取特定的数据字段。
-
Item Pipelines(数据管道):负责处理从爬虫中提取到的Item对象。通过定义一系列的Pipeline来对Item进行预处理、过滤、存储等操作,如数据清洗、去重、存储到数据库或文件等。
-
Downloader(下载器):负责下载网页内容并返回给Spider处理。Scrapy内置了一个高效的下载器,可以通过设置不同的配置来实现多线程、代理、网页策略等功能。
-
Scheduler(调度器):负责管理待抓取的URL队列,并根据一定的策略生成下一个要抓取的URL。Scrapy使用调度器来控制爬取流程,将请求分发给下载器,以便下载相应的网页内容。
-
Middleware(中间件):提供了对Scrapy请求和响应的处理机制,可以在请求和响应的过程中注入自定义的操作。通过中间件,可以对发出的请求和得到的响应进行修改和处理。
3.安装和创建
3.1 安装
pip install scrapy
3.2创建scrapy项目
scrapy startproject 项目名
- spiders文件夹专门用来放置爬虫程序,定义抓取特定网站的规则和逻辑,
- items.py用来定义要提取和保存的数据结构,
- pipelines.py用来处理数据,处理从爬虫提取的数据,如清洗、验证和存储等,
- settings.py来进行各种设置,配置Scrapy框架和爬虫参数,
- middlewares.py用来修改请求和响应的处理过程,如添加代理、设置User-Agent等。
cd 项目名
scrapy genspider 文件名 目标网站
3.3 爬取b站的数据
- 项目目录
- upinfo_spider.py
import scrapy
from scrapy.http import HtmlResponse
from bilibli_upinfo.items import UserBasicItem,UserOfficialItem,UserVipItem
class UpinfoSpider(scrapy.Spider):
name = "upinfo_spider"
allowed_domains = ["api.bilibili.com"]
uuids = ["1899427463","268975831","478548672","510027392"]
start_url = "https://api.bilibili.com/x/space/wbi/acc/info?mid=%(uuid)s"
def start_requests(self):
for userId in self.uuids:
yield scrapy.Request(url=self.start_url%{"uuid":userId},dont_filter=True)
def parse(self, response:HtmlResponse,**kwargs):
up_data = response.json()['data']
user_basic_item = UserBasicItem()
user_basic_item['up_name'] = up_data['name']
user_basic_item['up_mid'] = up_data['mid']
user_basic_item['up_gender'] = up_data['sex']
user_basic_item['up_avatar'] = up_data['face']
user_basic_item['up_intro'] = up_data['sign']
user_basic_item['up_level'] = up_data['level']
user_basic_item['up_registered_time'] = up_data['jointime']
user_basic_item['up_coins'] = up_data['coins']
user_basic_item['up_login_log'] = up_data['fans_badge']
user_basic_item['up_fans_medal'] = up_data['fans_medal']['show']
user_official_item = UserOfficialItem()
user_official_item['up_mid'] = up_data['mid']
user_official_item['official_role'] = up_data['official']['role']
user_official_item['official_title'] = up_data['official']['title']
user_official_item['official_desc'] = up_data['official']['desc']
user_official_item['official_type'] = up_data['official']['type']
user_vip_item = UserVipItem()
user_vip_item['up_mid'] = up_data['mid']
user_vip_item['vip_type'] = up_data['vip']['type']
user_vip_item['vip_status'] = up_data['vip']['status']
user_vip_item['vip_due_date'] = up_data['vip']['due_date']
user_vip_item['vip_pay_type'] = up_data['vip']['vip_pay_type']
user_vip_item['vip_theme_type'] = up_data['vip']['theme_type']
user_vip_item['vip_label_path'] = up_data['vip']['label']['path']
user_vip_item['vip_label_text'] = up_data['vip']['label']['text']
user_vip_item['vip_label_theme'] = up_data['vip']['label']['label_theme']
user_vip_item['vip_label_text_color'] = up_data['vip']['label']['text_color']
user_vip_item['vip_label_bg_style'] = up_data['vip']['label']['bg_style']
user_vip_item['vip_label_bg_color'] = up_data['vip']['label']['bg_color']
user_vip_item['vip_label_border_color'] = up_data['vip']['label']['border_color']
user_vip_item['vip_label_use_img'] = up_data['vip']['label']['use_img_label']
user_vip_item['vip_label_img_uri_hans'] = up_data['vip']['label']['img_label_uri_hans']
user_vip_item['vip_label_img_uri_hant'] = up_data['vip']['label']['img_label_uri_hant']
user_vip_item['vip_label_img_uri_hans_static'] = up_data['vip']['label']['img_label_uri_hans_static']
user_vip_item['vip_label_img_uri_hant_static'] = up_data['vip']['label']['img_label_uri_hant_static']
yield user_vip_item
yield user_basic_item
yield user_official_item
# #用户基础信息表
# print(f"UP主姓名:{up_name}")
# print(f"UP主ID:{up_mid}")
# print(f"UP主性别:{up_gender}")
# print(f"UP主头像链接:{up_avatar}")
# print(f"UP主简介:{up_intro}")
# print(f"UP主等级:{up_level}")
# print(f"UP主注册时间:{up_registered_time}")
# print(f"UP主硬币数:{up_coins}")
# print(f"UP主登录日志:{up_login_log}")
# print(f"UP主粉丝勋章:{up_fans_medal}")
#
# #认证信息表
# print(f"UP主ID:{up_mid}")
# print(f"UP主官方认证信息:{official_role}")
# print(f"UP主官方认证标题:{official_title}")
# print(f"UP主官方认证描述:{official_desc}")
# print(f"UP主官方认证类型:{official_type}")
#
# #会员信息表
# print(f"UP主ID:{up_mid}")
# print(f"UP主会员类型:{vip_type}")
# print(f"UP主会员状态:{vip_status}")
# print(f"UP主会员到期日期:{vip_due_date}")
# print(f"UP主会员支付类型:{vip_pay_type}")
# print(f"UP主会员主题类型:{vip_theme_type}")
# print(f"UP主会员标签路径:{vip_label_path}")
# print(f"UP主会员标签文本:{vip_label_text}")
# print(f"UP主会员标签主题:{vip_label_theme}")
# print(f"UP主会员标签文本颜色:{vip_label_text_color}")
# print(f"UP主会员标签背景样式:{vip_label_bg_style}")
# print(f"UP主会员标签背景颜色:{vip_label_bg_color}")
# print(f"UP主会员标签边框颜色:{vip_label_border_color}")
# print(f"UP主会员是否使用标签图片:{vip_label_use_img}")
# print(f"UP主会员标签图片URI(简体中文):{vip_label_img_uri_hans}")
# print(f"UP主会员标签图片URI(繁体中文):{vip_label_img_uri_hant}")
# print(f"UP主会员标签图片URI(简体中文,静态):{vip_label_img_uri_hans_static}")
# print(f"UP主会员标签图片URI(繁体中文,静态):{vip_label_img_uri_hant_static}")
- items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class UserBasicItem(scrapy.Item):
up_name = scrapy.Field()
up_mid = scrapy.Field()
up_gender = scrapy.Field()
up_avatar = scrapy.Field()
up_intro = scrapy.Field()
up_level = scrapy.Field()
up_registered_time = scrapy.Field()
up_coins = scrapy.Field()
up_login_log = scrapy.Field()
up_fans_medal = scrapy.Field()
class UserOfficialItem(scrapy.Item):
up_mid = scrapy.Field()
official_role = scrapy.Field()
official_title = scrapy.Field()
official_desc = scrapy.Field()
official_type = scrapy.Field()
class UserVipItem(scrapy.Item):
up_mid = scrapy.Field()
vip_type = scrapy.Field()
vip_status = scrapy.Field()
vip_due_date = scrapy.Field()
vip_pay_type = scrapy.Field()
vip_theme_type = scrapy.Field()
vip_label_path = scrapy.Field()
vip_label_text = scrapy.Field()
vip_label_theme = scrapy.Field()
vip_label_text_color = scrapy.Field()
vip_label_bg_style = scrapy.Field()
vip_label_bg_color = scrapy.Field()
vip_label_border_color = scrapy.Field()
vip_label_use_img = scrapy.Field()
vip_label_img_uri_hans = scrapy.Field()
vip_label_img_uri_hant = scrapy.Field()
vip_label_img_uri_hans_static = scrapy.Field()
vip_label_img_uri_hant_static = scrapy.Field()
- pipelines.py
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from traceback import format_exc
import pymysql
from itemadapter import ItemAdapter
from scrapy.utils.project import get_project_settings
from bilibli_upinfo.items import UserBasicItem, UserOfficialItem, UserVipItem
class BilibliUpinfoPipeline:
def __init__(self):
# 获取MySQL配置
mysql_host = get_project_settings().get('MYSQL_HOST')
mysql_port = get_project_settings().get('MYSQL_PORT')
mysql_database = get_project_settings().get('MYSQL_DATABASE')
mysql_user = get_project_settings().get('MYSQL_USER')
mysql_password = get_project_settings().get('MYSQL_PASSWORD')
# 连接MySQL数据库
self.connection = pymysql.connect(
host=mysql_host,
port=mysql_port,
database=mysql_database,
user=mysql_user,
password=mysql_password,
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
def process_item(self, item, spider):
try:
if isinstance(item,UserBasicItem):
# 处理用户基础信息表
with self.connection.cursor() as cursor:
sql = '''
INSERT INTO user_basic_info (up_name, up_mid, up_gender, up_avatar, up_intro, up_level, up_registered_time,
up_coins, up_login_log, up_fans_medal)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
'''
cursor.execute(sql, (
item['up_name'], item['up_mid'], item['up_gender'], item['up_avatar'], item['up_intro'],
item['up_level'],
item['up_registered_time'], item['up_coins'], item['up_login_log'], item['up_fans_medal']
))
self.connection.commit()
elif isinstance(item,UserOfficialItem):
# 处理认证信息表
with self.connection.cursor() as cursor:
sql = '''
INSERT INTO user_official_info (up_mid, official_role, official_title, official_desc, official_type)
VALUES (%s, %s, %s, %s, %s)
'''
cursor.execute(sql, (
item['up_mid'], item['official_role'], item['official_title'], item['official_desc'],
item['official_type']
))
self.connection.commit()
elif isinstance(item, UserVipItem):
# 处理会员信息表
with self.connection.cursor() as cursor:
sql = '''
INSERT INTO user_vip_info (up_mid, vip_type, vip_status, vip_due_date, vip_pay_type, vip_theme_type,
vip_label_path, vip_label_text, vip_label_theme, vip_label_text_color, vip_label_bg_style, vip_label_bg_color,
vip_label_border_color, vip_label_use_img, vip_label_img_uri_hans, vip_label_img_uri_hant,
vip_label_img_uri_hans_static, vip_label_img_uri_hant_static)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
'''
cursor.execute(sql, (
item['up_mid'], item['vip_type'], item['vip_status'], item['vip_due_date'], item['vip_pay_type'],
item['vip_theme_type'], item['vip_label_path'], item['vip_label_text'], item['vip_label_theme'],
item['vip_label_text_color'], item['vip_label_bg_style'], item['vip_label_bg_color'],
item['vip_label_border_color'], item['vip_label_use_img'], item['vip_label_img_uri_hans'],
item['vip_label_img_uri_hant'], item['vip_label_img_uri_hans_static'],
item['vip_label_img_uri_hant_static']
))
self.connection.commit()
except:
print(item)
print(format_exc())
def close_spider(self, spider):
self.connection.close()
- settings.py
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = { # 请求的头部
'origin': "https://space.bilibili.com",
'referer': "https://space.bilibili.com/",
'user-agent': "*********自己的********",
'Host': "api.bilibili.com"
}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
"bilibli_upinfo.pipelines.BilibliUpinfoPipeline": 300,
}
MYSQL_HOST = '127.0.0.1' # 主机名
MYSQL_PORT = 3306 # 端口号
MYSQL_DATABASE = 'bilibili_users' # 数据库名
MYSQL_USER = 'root' # 用户名
MYSQL_PASSWORD = '123456' # 密码
- main.py
def run_cmd_Popen_fileno(cmd_string):
"""
执行cmd命令,并得到执行后的返回值,python调试界面输出返回值
:param cmd_string: cmd命令,如:'adb devices'
:return:
"""
import subprocess
print('运行cmd指令:{}'.format(cmd_string))
return subprocess.Popen(cmd_string, shell=True).wait()
if __name__ == '__main__':
run_cmd_Popen_fileno("scrapy crawl upinfo_spider")
3.3.1 游标
在使用Python操作数据库时,使用游标(Cursor)是非常重要的。游标可以看作是与数据库直接交互的对象,通过游标可以执行SQL语句、获取执行结果等操作。
使用游标的主要原因是为了实现与数据库的交互和操作。通过游标,可以执行SQL语句对数据库进行不同的操作,如插入、查询、更新、删除等。游标还提供了获取查询结果和处理查询结果的方法,如fetchone()和fetchall()来获取查询结果的一行或多行数据。
具体原因如下:
-
执行多个SQL语句:游标可以执行多个SQL语句,执行的SQL语句可以是插入、更新、删除等操作。通过游标可以逐个执行这些操作,并将其提交到数据库中。
-
提交事务:数据库操作通常是在事务中进行的,事务是一个数据库操作序列,要么全部执行成功,要么全部执行失败。游标提供了
commit()
方法,可以将操作立即提交到数据库中。 -
获取执行结果:游标还可以获取执行SQL语句后的结果,如查询结果。通过使用fetchone()和fetchall()等方法,可以获取到查询结果的行数据或所有数据。
-
错误处理:通过游标可以捕获数据库操作的错误,如插入重复数据、数据插入长度超过限制等情况。异常处理可以帮助开发者快速定位问题,并做相应的处理。
总之,通过创建游标,可以更好地与数据库进行交互和操作,并能够获取执行结果和进行错误处理。这样可以有效地管理和控制对数据库的操作,确保数据的安全和完整性。
3.1.2 解析器
解析器是指用于解析和处理文档的工具或程序。在爬虫中,解析器主要用于解析网页内容,从中提取所需的数据。
常见的解析器包括XPath和CSS选择器。
-
XPath:XPath是一种用于在XML或HTML文档中进行导航和提取数据的解析语言。它使用路径表达式来定位和选择节点,可以按照层级结构以及属性等进行筛选和定位。XPath可以通过节点名称、属性、文本内容等进行节点的定位,并提供丰富的函数和操作符用于节点的筛选和提取。在Scrapy中,使用XPath可以非常方便地从网页中提取所需的数据。
-
CSS选择器:CSS选择器是一种通过样式选择器来筛选和选择节点的方式,常用于网页的样式设计。在爬虫中,可以借用CSS选择器来定位和提取网页中的元素。CSS选择器与XPath相比更简洁,容易理解和使用。Scrapy中也提供了对CSS选择器的支持,使得开发者可以根据CSS选择器来定位和提取数据。
解析器的选择取决于个人偏好和具体的应用场景。XPath和CSS选择器都是强大而灵活的工具,可以根据需求选择适合的解析器。在爬虫开发中,合理运用解析器能够更快地定位和提取所需的数据,提高爬虫的效率和准确性。
总结起来,这个完整的爬虫代码利用Scrapy框架实现了网页的下载、解析和数据提取,使用了XPath和CSS选择器来定位和提取所需的数据。通过自定义的中间件和Item Pipeline,实现了对请求和响应的处理以及数据的存储。这些关键技术的使用帮助实现了一个高效、可扩展的网络爬虫。