爬取豆瓣电影 top250movie.douban.com/top250的电影数据,并保存在数据库中。
1.items.py文件:自定义字段,确定要爬取的目标网站数据
import scrapy
class DoubanItem(scrapy.Item):
# 标题
title = scrapy.Field()
# 是否可播放的状态
playable = scrapy.Field()
# 简介
content = scrapy.Field()
# 评分
star = scrapy.Field()
# 评论数量
commentnum = scrapy.Field()
# 主题
inq = scrapy.Field()
2.spiders/douban.py 文件: 爬虫文件,在这里编写爬虫代码,解析数据,获取新的url
import scrapy
from douban.items import DoubanItem
class DoubanspiderSpider(scrapy.Spider):
name = 'doubanspider'
allowed_domains = ['douban.com']
#可以设置偏移量,也可以提取网站页面源码中下一页
#标签的链接,获取到下一页url地址
offset = 0
url = 'https://movie.douban.com/top250?start='
start_urls = [url + str(offset) + "&filter="]
def parse(self, response):
itemlist = response.xpath("//div[@class='item']")
for each in itemlist:
item = DoubanItem()
# 标题
title = each.xpath(".//div[@class='info']/div[@class='hd']//span[@class='title']/text()").extract_first("")
# 是否可播放的状态
playable = each.xpath(".//div[@class='info']/div[@class='hd']/span[@class='playable']/text()").extract_first("")
# 简介
content = each.xpath(".//div[@class='info']//div[@class='bd']/p/text()").extract()
content = "".join(content).strip()
# 评分
star = each.xpath(".//div[@class='star']//span[@class='rating_num']/text()").extract_first("")
# 评论数量
commentnum = each.xpath(".//div[@class='star']/span[4]/text()").extract_first("")
# 主题
inq = each.xpath(".//div[@class='info']//div[@class='bd']//span[@class='inq']/text()").extract_first("")
item['title'] = title
item['playable'] = playable
item['content'] = content
item['star'] = star
item['commentnum'] = commentnum
item['inq'] = inq
yield item
#下一页(方式一)
if self.offset < 250:
self.offset += 25
yield scrapy.Request(self.url + str(self.offset) +"&filter=",callback= self.parse)
3.数据持久化
方式一:将数据存入mongodb
settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...
ITEM_PIPELINES = {
'douban.pipelines.DoubanPipeline': 300,
}
MONGODB 主机名
MONGODB_HOST = '127.0.0.1'
MONGODB 端口号
MONGODB_PORT= 27017
数据库名称
MONGODB_DBNAME = "Douban"
存储数据的表名称
MONGODB_SHEETNAME= "doubanmovies"
4.pipelines.py管道:这里进行数据的清洗和持久化
import pymongo
from scrapy.conf import settings
class DoubanPipeline(object):
# 将数据存储在mongodb中
def __init__(self,host,port,dbname,sheetname):
# 创建MONGODB数据库链接
client = pymongo.MongoClient(host=host, port=port)
# 指定数据库
mydb = client[dbname]
# 存放数据的集合名称
self.mysheet = mydb[sheetname]
@classmethod
def from_crawler(cls, crawler):
host = crawler.settings["MONGODB_HOST"]
port = crawler.settings["MONGODB_PORT"]
dbname = crawler.settings["MONGODB_DBNAME"]
sheetname = crawler.settings["MONGODB_SHEETNAME"]
return cls(host,port,dbname,sheetname)
def process_item(self,item,spider):
data = dict(item)
# mongodb数据插入语句,使用save保存数据的效率会很慢,因为它需要循环便利,操作费时
self.mysheet.insert(data)
return item
方式二:将数据存入mysql数据库
settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...
ITEM_PIPELINES = {
'douban.pipelines.DoubanPipeline': 300,
}
#关于数据库的相关配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = ''
MYSQL_PWD = ''
MYSQL_DB = ''
pipelines.py管道文件
import pymysql
class DoubanPipeline(object):
# 将数据存储值mysql数据库
# _mysql_exceptions.OperationalError: (1366, 是因为数据库中的字符集与charset="utf8"不符合
def __init__(self,host,port,user,pwd,db,charset):
self.client = pymysql.Connect(host,user,pwd,db,port,charset='utf8')
self.cursor = self.client.cursor()
@classmethod
def from_crawler(cls,crawler):
host = crawler.settings['MYSQL_HOST']
port = crawler.settings['MYSQL_PORT']
user = crawler.settings['MYSQL_USER']
pwd = crawler.settings['MYSQL_PWD']
db = crawler.settings['MYSQL_DB']
return cls(host,port,user,pwd,db,charset)
def process_item(self,item,spider):
insert_sql = """
insert into doubanmovie(title, playable, content, star, commentnum, inq)
VALUE (%s, %s, %s, %s, %s, %s)
"""
try:
self.cursor.execute(insert_sql, (item['title'], item['content'], item['score'], item['info']))
self.client.commit()
except Exception as err:
print(err)
self.client.rollback()
return item
5.思考???: 在pipeline中我们进行数据插入,如何解耦?
将sql语句和要插入的数据在item中定义一个方法返回,通过item调用,然后返回
class XxxxItem(scrapy.Item):
#名称
title = scrapy.Field()
def insert_data_to_db(self,dataDict):
sql = """
INSERT INTO caipu (%s)
VALUES (%s)
""" % (','.join(dataDict.keys()),','.join(['%s']*len(dataDict)))
data = list(dataDict.values())
return sql,data
mysql数据异步插入
settings.py配置文件
#将mysql相关信息写在settings中
MYSQL_HOST = '127.0.0.1'
MYSQL_ROOT = '数据库用户名'
MYSQL_PASSWORD = '数据库密码'
MYSQL_DBNAME = 'DouBan'
6.异步插入数据库
import pymysql
#twisted是一个异步的网络框架,这里可以帮助我们
实现异步将数据插入数据库
#adbapi里面的子线程会去执行数据库的阻塞操作,
当一个线程执行完毕之后,同时,原始线程能继续
进行正常的工作,服务其他请求。
from twisted.enterprise import adbapi
#异步插入数据库
class DoubanPipeline(object):
def __init__(self,dbpool):
self.dbpool = dbpool
#使用这个函数来应用settings配置文件。
@classmethod
def from_crawler(cls, crawler):
parmas = {
'host':crawler.settings['MYSQL_HOST'],
'user':crawler.settings['MYSQL_USER'],
'passwd':crawler.settings['MYSQL_PASSWD'],
'db':crawler.settings['MYSQL_DB'],
'port':3306,
'charset':'utf8',
}
# **表示字典,*tuple元组,
# 使用ConnectionPool,起始最后返回的是一个ThreadPool
dbpool = adbapi.ConnectionPool(
'pymysql',
**parmas
)
return cls(dbpool)
def process_item(self, item, spider):
#这里去调用任务分配的方法
query = self.dbpool.runInteraction(
self.insert_data_todb,
item,
spider
)
#数据插入失败的回调
query.addErrback(
self.handle_error,
item
)
#执行数据插入的函数
def insert_data_todb(self,cursor,item,spider):
insert_str,parmas = item.insertdata()
cursor.execute(insert_str,parmas)
print('插入成功')
def handle_error(self,failure,item):
print(failure)
print('插入错误')
#在这里执行你想要的操作
def close_spider(self, spider):
self.pool.close()