Web+爬虫
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。自己写Python爬虫程序不是不可,但有框架可以用,为什么不用呢?相信Scrapy可以起到事半功倍的效果,相比与request,scrapy重点在于爬虫框架而不是页面下载。
scrapy爬虫需要的库pypiwin32,lxml,twisted,scrapy,Microsoft Visual C++ 14.0以上 编译环境
数据库连接模块,pymysql,以上这些库我都是使用pip安装的,我把清华的镜像源放在这里-i https://pypi.tuna.tsinghua.edu.cn/simple
首先,引擎从调度器中取出一个链接(URL)用于接下来的抓取
1.引擎把URL封装成一个请求(Request)传给下载器,下载器把资源下载下来,并封装成应答包(Response)
2.然后,爬虫解析Response。
3.若是解析出实体(Item),则交给实体管道进行进一步的处理。
4.若是解析出的是链接(URL),则把URL交给Scheduler等待抓取。
使用命令 scrapy startproject projectname
PS D:\pythonpractice> scrapy startproject douban
New Scrapy project 'douban', using template directory 'D:\python38install\Lib\site-packages\scrapy\templates\project', created in:
D:\pythonpractice\douban
You can start your first spider with:
cd douban
scrapy genspider example example.com
我使用的是vscode,使用ctrl和·键唤起终端,输入命令,我准备爬取豆瓣网的一些页面,所以起名douban,可以看到,在你打开的文件夹下面出现了一个新的文件夹,名字便是我起的那个
之后使用cd douban命令,进入项目所在的的文件夹中
PS D:\pythonpractice> cd douban
PS D:\pythonpractice\douban>
在项目的文件夹中可以看到还有一个与项目同名的文件夹,里面有一些文件,
1.items.py负责数据模型的建立,设置数据存储模板,用于结构化数据,如:Django的Model
2.middlewares.py是自己定义的中间件
3.pipelines.py负责对spider返回数据的处理,如进行数据持久化
4.settings.py负责对整个爬虫的配置
5.spiders目录负责存放继承自scrapy的爬虫类
6.scrapy.cfg是scrapy基础配置
使用scrapy genspider [options] 创建一个自己的爬虫,
我输入了scrapy genspider book douban.com,即创建一个叫book的爬虫,domain部分是爬虫是使用范围,所爬取的网页不能超出这个域名。
修改item.py如下
import scrapy
class DoubanItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
img_src = scrapy.Field()
score = scrapy.Field()
publish_house = scrapy.Field()
publish_detail = scrapy.Field()
slogan = scrapy.Field()
detail_addr = scrapy.Field()
comment_people = scrapy.Field()
打开网址链接: 豆瓣读书 Top 250(https://book.douban.com/top250)
可以看到结构是非常清晰的,而我需要的便是这一本本书的信息,编写book.py如下,
class BookSpider(scrapy.Spider):
name = 'book'
allowed_domains = ['douban.com']
start_urls = ['https://book.douban.com/top250?start=0']
url_sets = set()
def parse(self, response):
if response.url.startswith('https://book.douban.com'):
seclectors = response.xpath('//div[@class="indent"]/table')
for seclector in seclectors:
item = DoubanItem()
item['title'] = seclector.xpath("./tr/td[2]/div/a/@title").extract()[0]
item['img_src'] = seclector.xpath("./tr/td[1]/a/img/@src").extract()[0]
item['publish_detail'] = seclector.xpath("./tr/td[2]/p[@class='pl']/text()").extract()[0]
item['score'] = seclector.xpath("./tr/td[2]/div[@class='star clearfix']/span[@class='rating_nums']/text()").extract()[0]
item['detail_addr'] = seclector.xpath("./tr/td[2]/div[@class='pl2']/a/@href").extract()[0]
#标语爬取
item['slogan'] = seclector.xpath("./tr/td[2]/p[@class='quote']/span/text()").get()
if item['slogan']:
pass
else:
item['slogan'] = '暂无信息'
#评论人数爬取
item['comment_people'] = seclector.xpath("./tr/td[2]/div[@class='star clearfix']/span[@class='pl']/text()").extract()[0]
comment = str(item['comment_people'])
rex = '[0-9]+'
num = re.findall(rex, comment)
if num:
item['comment_people'] = num[0]
else:
item['comment_people'] = '暂无信息'
#出版社爬取
t = str(item['publish_detail'])
s = t.encode() #进行字符串转义
temp = s.decode('utf-8')
pattern="[\u4e00-\u9fa5]*\u51fa\u7248[\u4e00-\u9fa5]*|[\u4e00-\u9fa5]*\u4E66\u5E97[\u4e00-\u9fa5]*" #中文 unicode编码出版 正则表达式
results = re.findall(pattern, temp) #匹配
if results:
item['publish_house'] = results[0]
else:
item['publish_house'] = '暂无信息'
yield item
#分页处理
urls = response.xpath("//div[@class='paginator']/span[@class='next']/a/@href").extract()
for url in urls:
if url.startswith('https://book.douban.com'):
if url in self.url_sets:
pass
else:
self.url_sets.add(url)
yield self.make_requests_from_url(url)
else:
pass
(1)其中start_urls是起始页面,后续页面从网页的分页器得到,不过如果网站使用的是js进行分页,那就没有办法了。
(2)抽取网页内容有很多方法,比如beautifulsoup、lxml,但是scrapy里面默认使用的是selector,相对来说也是最好用的对页面使用xpath进行分析,大家可以自己查找了解xpath。只是用于爬虫的网页分析的话感觉不是很难。
(3)对于书籍的封面图片在得到它的地址后是可以选择把它保存在本地的,但由于我感觉不喜欢往我这个个人pc端的mysql放图片,总感觉不是很靠谱,就只将它的地址爬取了下来,如果自己写网站想用这些图片的话,直接使用地址应该也是可以的。
(4)出版社信息爬取部分,由于这是由作者/出版社/日期/价格组成的,我使用了正则表达式将含有“出版社”,“书店”等关键字的字符串提取出来,便于在将数据持久化以后进行数据分析。
编写pipline.py如下
from itemadapter import ItemAdapter
import urllib.request
import os
import pymysql
class DoubanPipeline:
def process_item(self, item, spider):
#保存图片
# url = item['img_addr']
# req = urllib.request.Request(url)
# with urllib.request.urlopen(req) as pic:
# data = pic.read()
# file_name = os.path.join(r'D:\bookpic',item['name'] + '.jpg')
# with open(file_name, 'wb') as fp:
# fp.write(data)
#保存到数据库
info = [item['title'], item['score'], item['publish_detail'], item['slogan'], item['publish_house'], item['img_src'], item['detail_addr'], item['comment_people']]
connection = pymysql.connect(host='localhost', user='root', password='', database='', charset='utf8')
try:
with connection.cursor() as cursor:
sql = 'insert into app_book (title, score, publish_detail, slogan, publish_house, img_src, detail_addr, comment_people) values (%s, %s, %s, %s, %s, %s, %s, %s)'
affectedcount = cursor.execute(sql, info)
print('成功修改{0}条数据'.format(affectedcount))
connection.commit()
except pymysql.DatabaseError:
connection.rollback()
finally:
connection.close()
return item
settings.py增加如下内容
(1)设置处理返回数据的类及执行优先级
ITEM_PIPELINES = {
'douban.pipelines.DoubanPipeline': 100,
}
(2)添加User-Agent
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) G ecko/20100101 Firefox/52.0'
}
输入命令scrapy crawl book,爬虫开始执行