本文正在参加「Python主题月」,详情查看 活动链接
前言
scrapy是一个非常成熟的爬虫框架,里面几乎封装好了开发者所需要的所有模块,例如:request,代理,日志,url自动去重等等,一些不太满意的模块也是稍加修改就行。但scrapy只能爬静态网页,如果需要爬动态渲染的网页的话还需要借助第三方组件,例如selenium或者splash.
为什么选择selenium
selenium和splash都是非常优秀的动态网页渲染工具。但selenium与splash相比,主要有以下几个有点:
- selenium可以直接在浏览器运行,就像真是的用户操作一样。
- selenium的文档更加详细,国内的教程也多,splash教程真是少的可怜。
- selenium支持的模拟用户行为的操作命令更加全面详细,splash的部分用户操作行为也有,但具体怎么使用,通过他们的手册我是没看懂。
综上几点还是选择selenium好一点,但并不是splash全无优点,我之前写过PHP的爬虫,用的就是splash,个人意见:如果没有特别复杂的交互,用splash还是可以的,splash提供的api,我们只需要把请求头,代理和url传给splash,也能得到渲染好的html.
本文要实现的功能
- Scrapy的安装
- 配置selenium
- sqlalchemy完成数据入库
Scrapy的安装
安装scrapy
在终端输入 pip install scrapy
创建scrapy项目
- 创建项目,命令:
scrapy startproject 项目名称
- 创建爬虫文件,命令:
scrapy genspider 文件名称 域名
我执行的
scrapy startproject spider
复制代码
scrapy genspider douban douban.com
复制代码
生成的项目目录就是这样的
spider
spider
spiders
__init__.py
douban.py
__init__.py
items.py
middlewares.py
piplines.py
settings.py
scrapy.cfg
复制代码
添加相关目录
spider
spider
common # 公共配置文件目录
__init__.py
conf.py # 数据库配置信息
models # mysql model类
__init__.py
spiders
__init__.py
douban.py
__init__.py
items.py
middlewares.py
piplines.py
settings.py
scrapy.cfg
复制代码
配置selenium
浏览器驱动的安装就不说了,因为每个系统安装方式都不一样,网上各个系统的安装方法都很详细,也不是很难。
引入selenium包
pip安装selenium包
pip install selenium
复制代码
更改代码
spider/spiders/douban.py
import scrapy
from selenium import webdriver
# 使用无头浏览器
from selenium.webdriver.chrome.options import Options
# 无头浏览器设置
chorme_options = Options()
chorme_options.add_argument("--headless")
chorme_options.add_argument("--disable-gpu")
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['douban.com']
start_urls = ['https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0']
# 实例化一个浏览器对象
def __init__(self):
self.browser = webdriver.Chrome(options=chorme_options)
super().__init__()
def parse(self, response):
print(response.body)
pass
def close(self, spider):
print('爬虫结束,关闭浏览器')
self.browser.quit()
复制代码
settings.py配置文件里更改一些配置
# 更改用户代理(也可以设置成动态切换,个人感觉没必要)
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
# 关闭遵从robots协议
ROBOTSTXT_OBEY = False
# 打开下载中间件
DOWNLOADER_MIDDLEWARES = {
'spider.middlewares.SpiderDownloaderMiddleware': 543,
}
# 数据处理管道
ITEM_PIPELINES = {
'spider.pipelines.SpiderPipeline': 300,
}
复制代码
middlewares.py中间件文件修改:
SpiderDownloaderMiddleware类的process_response方法修改,用浏览器驱动加载url
def process_response(self, request, response, spider):
"""
三个参数:
# request: 响应对象所对应的请求对象
# response: 拦截到的响应对象
# spider: 爬虫文件中对应的爬虫类的实例对象, 可以通过这个参数拿到中的一些属性或方法
"""
spider.browser.get(url=request.url)
# 等待加载, 可以用显示等待来优化.
time.sleep(1)
row_response = spider.browser.page_source
# 参数url指当前浏览器访问的url(通过current_url方法获取), 在这里参数url也可以用request.url
return HtmlResponse(url=spider.browser.current_url, body=row_response, encoding="utf8",
request=request)
复制代码
至此,selenium的配置就完了
引入sqlalchemy完成数据入库
在Python中,最有名的ORM框架是SQLAlchemy,文档齐全使用方便,同时为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。
添加数据库连接配置文件
数据库配置文件spider/common/conf.py
class conf:
mysql = {
# 数据库本地环境配置信息
'dev': {
'host': '127.0.0.1',
'database': 'test',
'user': 'user',
'password': 'passord',
'port': 3306,
},
# 测试服务器配置信息
'test': {
'host': '127.0.0.1',
'database': 'test',
'user': 'user',
'password': 'passord',
'port': 3306,
},
# 线上服务器配置信息
'prod': {
'host': '127.0.0.1',
'database': 'test',
'user': 'user',
'password': 'passord',
'port': 3306,
},
}
}
复制代码
运行环境配置文件
spider/common/env.py
# coding:utf-8
"""
环境标识(不可随意更改,此文件不上传到git)
本地环境 dev
测试环境 test
线上环境 prod
"""
class env:
env = 'dev'
复制代码
添加数据库连接
spider/models/__init__.py
文件里添加数据库连接
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from spider.common.conf import conf
from spider.common.env import env
Base = declarative_base()
env = env.env
mysql = conf.mysql[env]
engine = create_engine(
url="mysql+pymysql://{user}:{password}@{host}/{database}".format(
user=mysql['user'],
password=mysql['password'],
host=mysql['host'],
database=mysql['database']),
encoding='UTF-8',
pool_size=5, # 连接池大小
max_overflow=10, # 超过连接池大小外最多创建的连接
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_pre_ping=True
)
Session = sessionmaker(bind=engine)()
复制代码
建立数据表模型
spider/models
目录下添加Movie.py
文件
from sqlalchemy import Column, String, Integer, DateTime
from spider.models import Base
class Movie(Base):
# 表公共信息配置
__tablename__ = "movie"
# 表字段定义
id = Column(Integer, primary_key=True)
title = Column(String(100), comment='影片名')
created_at = Column(DateTime, comment='添加时间')
updated_at = Column(DateTime, comment='更新时间')
复制代码
添加item字段
items.py
添加MovieItem
import scrapy
class SpiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class MovieItem(scrapy.Item):
title = scrapy.Field()
pass
复制代码
更改爬虫程序
spider/spiders/douban.py
添加数据解析
import scrapy
from selenium import webdriver
# 使用无头浏览器
from selenium.webdriver.chrome.options import Options
# 无头浏览器设置
from spider.items import MovieItem
chorme_options = Options()
chorme_options.add_argument("--headless")
chorme_options.add_argument("--disable-gpu")
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['douban.com']
start_urls = ['https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0']
# 实例化一个浏览器对象
def __init__(self):
self.browser = webdriver.Chrome(options=chorme_options)
super().__init__()
def parse(self, response):
for value in response.css('.list-wp>.list>a'):
item = MovieItem()
item['title'] = value.css('.cover-wp>img::attr(alt)').extract_first()
yield item
pass
def close(self, spider):
print('爬虫结束,关闭浏览器')
self.browser.quit()
复制代码
管道入库
pipelines.py
将数据添加到数据库
import datetime
from spider.models import Session
from spider.models.Movie import Movie
class SpiderPipeline:
"""
爬虫被打开的时候执行
:param spider:
:return:
"""
def open_spider(self, spider):
print("爬虫开始....")
"""
爬虫有item传过来的时候会被调用
:param item:
:param spider:
:return:
"""
def process_item(self, item, spider):
try:
data = Movie(title=item['title'], created_at=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
updated_at=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
Session.add(data)
Session.commit()
except():
Session.rollback()
return None
"""
爬虫关闭的时候被调用
:param spider:
:return:
"""
def close_spider(self, spider):
# 关闭数据库连接
Session.close()
print("爬虫结束,关闭数据库连接")
复制代码
总结
至此一个简单的爬虫程序就完成了,上面所有的请求都是走selenium组件的,有些静态网页是不需要的,在中间件里判断一下就行了。本篇只是简单的封装,完善的爬虫程序还需要设置代理,考虑分布式,增量爬虫,这里就不深入介绍了。