简介
安装scrapy
pip install scrapy -i https://pypi.douban.com/simple
创建scrapy项目
项目组成
spiders
__init__.py
自定义的爬虫文件.py: 由我们自己创建,是实现爬虫核心功能的文件
__init__.py
itmes.py: 定义数据结构的地方,是一个继承自scrapy.Item的累
middlewares.py: 中间件——代理
pipelines.py: 管道文件,里面只有一个类,用于处理下载数据的后续处理,默认是300优先级,值越小优先级越高(1-1000)
settings.py: 配置文件,比如:是否遵守robots协议,User-Agent定义等
创建爬虫文件
爬虫文件的基本组成
继承scrapy.Spider类
name = "baidu" -> 运行爬虫文件时使用的名字
allowed_domains -> 爬虫允许的域名,在爬取的时候,如果不是此域名之下的url,会被过滤掉
start_urls -> 声明了爬虫的起始地址,可以写多个url,一般是一个
parse(self,response) -> 解析数据的回调函数
```python import scrapy
class BaiduSpider(scrapy.Spider):
# 爬虫的名字,用于运行爬虫的时候使用的值
name = "baidu"
# 允许访问的域名
allowed_domains = ["www.baidu.com"]
# 起始的url地址,指的是第一次要访问的域名
# start_urls 是在 allowed_domains的前面添加一个http://,在后面添加一个/
start_urls = ["http://www.baidu.com/"]
# 是执行了start_urls之后执行的方法,方法中的response就是返回的那个对象
# 相当于 response = urllib.request.urlopen()——response = requests.get()
def parse(self, response):
print("学习漫长的代码路程中...")
- 运行爬虫文件:
- scrapy crawl 爬虫名称

**注意点:在settings文件中,有存在遵守robots协议,所以访问的域名为百度时则无法有预期的响应效果**
```python
# robots协议:各大网站中商讨的一个协议(互相都不爬,遵守则设置该值 = True)【一般情况下,不用遵守!】
# ROBOTSTXT_OBEY = True
当Item在Spider中被收集后,它将会被传递到Item Pipline,一些组件会按照一定的顺序执行对Item的处理。每个Item pipeline组件是实现了简单方法的Python类。他们接受到Item并通过它执行一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不在进行处理
import scrapy
class CarSpider(scrapy.Spider):
name = "car"
allowed_domains = ["car.autohome.com.cn/price/brand-15.html"]
start_urls = ["https://car.autohome.com.cn/price/brand-15.html"]
def parse(self, response):
print("=============================")
names = response.xpath("//div[@class='main-title']/a/text()")
money = response.xpath("//span[@class='font-arial']/text()")
for i in range(len(names)):
print(f"车名:{names[i].extract()}——价格:{money[i].extract()}")
概述
安装
应用——cmd
scrapy shell www.baidu.com
scrapy shell http://www.baidu.com
scrapy shell "www.baidu.com"
语法
response对象:
response.text -二进制网页数据
response.body -网页数据
response.url -url
response.status -状态码
response的解析:
response.xpath()【常用】
使用xpath路径查询特定元素,返回一个selector列表对象
response.css()
使用css_selector查询元素,返回一个selector列表对象
获取内容:response.css("#su::text").extract_first()
获取属性:response.css("#su::arrt('value')").extract_first()
selectort对象(通过xpath方法调用返回的是selector列表)
extract()
提取selector对象的值【提取不到报错】
extract_first()
提取selector列表中的第一个值【取不到会返回空值】
【注意:每一个selector对象都可以再次去使用xpath或者css方法】
带有yield的函数不在是一个普通函数,而是一个生成器generator,可用于迭代
yield是一个类似 return的关键字,迭代一次遇到yield时就返回yield后面的值,下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行
案例:当当网
```python import scrapy
from scrapy_four_dangdang.items import ScrapyFourDangdangItem
class DangdangSpider(scrapy.Spider): name = "dangdang"
# 如果是多页下载的话,那么必须要调整的是allowed_domains的范围,一般情况下只写域名
allowed_domains = ["category.dangdang.com"]
start_urls = ["http://category.dangdang.com/cp01.01.02.00.00.00.html"]
# 第二页:http://category.dangdang.com/pg2-cp01.01.02.00.00.00.html
# 第三页:http://category.dangdang.com/pg3-cp01.01.02.00.00.00.html
baseUrl = "http://category.dangdang.com/pg"
page = 1
def parse(self, response):
# piplines 下载数据
# items 定义数据结构
# 定位信息:定位src图片资源时,img取src属性时会导致都是同一张图片
# 原因:懒加载——真正的图片路径是@data-original
# src = response.xpath("//ul[@id='component_59']/li//img/@data-original")
# name = response.xpath("//ul[@id='component_59']/li//img/@alt")
# price = response.xpath("//ul[@id='component_59']/li//p[@class='price']/span[1]/text()")
# 所有的selector对象都可以再次调用xpath——将共同部分抽取出来
li_List = response.xpath("//ul[@id='component_59']/li")
for i in li_List:
# 第一个src时,会发现取到None,因为网页中第一个的图片资源为img 而其它为懒加载data_original
src = i.xpath(".//img/@data-original").extract_first()
if src:
src = src
else:
src = i.xpath(".//img/@src").extract_first()
name = i.xpath(".//img/@alt").extract_first()
price = i.xpath(".//p[@class='price']/span[1]/text()").extract_first()
book = ScrapyFourDangdangItem(src=src,name=name,price=price)
# yield 获取一个book,则返回一个book到piplelines去下载
yield book
if(self.page<100):
self.page+=1
url = f"{self.baseUrl}{self.page}-cp01.01.02.00.00.00.html"
# 怎么去调用parse方法继续请求获取数据并下载
# scrapy.Request就是scrpay的get请求(scrapy.Request(url,callback)),url就是请求地址 callback就是要执行的函数注意不加()
yield scrapy.Request(url=url,callback=self.parse)
- items.py
```python
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class ScrapyFourDangdangItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 通俗说,itmes就是要下载的数据都有什么
src = scrapy.Field()
name = scrapy.Field()
price = scrapy.Field()
pipelines.py
# 如果要使用管道,需要在settings中开启
class ScrapyFourDangdangPipeline:
# 在爬虫文件开始执行前执行该方法
def open_spider(self,spider):
self.f = open("./book.json","w",encoding="UTF-8")
print("======================================")
# item就是yield后面的book对象
def process_item(self, item, spider):
# 在执行前打开文件,执行完毕后关闭文件来解决频繁操作文件的问题
self.f.write(str(item))
# 以下这种方式不推荐,因为每传递一个对象,就打开一次文件,对文件的操作过于频繁
"""
1.write()方法必须写字符串,不能是对象
2.操作文件的方式为 a 追加状态,因为打开一次则会覆盖前面的的内容
with open("./book.json","a",encoding="UTF-8") as f:
f.write(str(item))
"""
return item
# 在爬虫文件开始执行后执行该方法
def close_spider(self,spider):
print("======================================")
self.f.close()
# 开启多条疏通管道
# 1.定义管道类
# 2.在settings中开启管道———— "scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline_img":301,
class ScrapyFourDangdangPipeline_img:
def process_item(self, item, spider):
url = f"http:{item.get('src')}"
fileName = f"./booksImg/{item.get('name')}.jpg"
import urllib.request
urllib.request.urlretrieve(url=url,filename=fileName)
return item
settings.py【打开疏通管道,并设置多条疏通管道】
ITEM_PIPELINES = {
# 管道可以有很多个,存在优先级,范围(1-1000)值越小,优先级越高
"scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline": 300,
"scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline_img":301,
}
概述
提取链接
scrapy.linkextractors.LinkExtractor(
allow = (), # 正则表达式,提取符合正则的链接
deny = (), # (不用)正则表达式,不提去符合正则的链接
allow_domains = () # (不用)允许的域名
deny_domains = () # (不用)不允许的域名
restrict_xpaths = ()# xpath,提取符合xpath规则的链接
restrict_css = () # 提取符合选择器规则的链接
)
使用
读书网数据入库
创建项目
1.创建项目 scrapy startproject scrapy_six_readBook
2.切换到spiders cd scrapy_six_readBook\scrapy_six_readBook\spiders
3.创建爬虫类 scrapy gensipder -t crawl read www.dushu.com
read.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_six_readBook.items import ScrapySixReadbookItem
class ReadSpider(CrawlSpider):
name = "read"
allowed_domains = ["www.dushu.com"]
# 爬取数据时,第一页的数据回导致丢失,因为路径不存在正则表达式当中【注意】 1188_1
start_urls = ["http://www.dushu.com/book/1188_1.html"]
rules = (Rule(LinkExtractor(allow=r"/book/1188_\d+\.html"), callback="parse_item", follow=True),)
def parse_item(self, response):
# 爬取书的名字和图片
list = response.xpath("//div[@class='bookslist']//img")
for i in list:
name = i.xpath("./@alt").extract_first()
img = i.xpath("./@data-original").extract_first()
book = ScrapySixReadbookItem(name=name,img=img)
yield book
class ScrapySixReadbookItem(scrapy.Item):
name = scrapy.Field()
img = scrapy.Field()
class ScrapySixReadbookPipeline:
def open_spider(self,spider):
self.f = open("./book.json","w",encoding="UTF-8")
def process_item(self, item, spider):
self.f.write(str(item))
return item
def close_spider(self,spider):
self.f.close()
import pymysql
class ScrapySixReadbookPipeline_MySQL:
def open_spider(self,spider):
# 创建链接
self.conn = pymysql.connect(
host="localhost",
port=3306,
user="root",
password="root",
charset="utf8"
)
# 选择数据库
self.conn.select_db("python")
# 获取游标
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
# sql = "insert into readbook(name,img) values('{}','{}')".format(item["name"],item["img"])
sql = f"insert into readbook(name,img) values('{item['name']}','{item['img']}')"
# print(sql)
self.cursor.execute(sql)
self.conn.commit()
return item
def close_spider(self,spider):
self.cursor.close()
self.conn.close()
ITEM_PIPELINES = {
"scrapy_six_readBook.pipelines.ScrapySixReadbookPipeline": 300,
# ScrapySixReadbookPipeline_MySQL
"scrapy_six_readBook.pipelines.ScrapySixReadbookPipeline_MySQL":301,
}
日志级别
默认的日志等级是DEBUG
settings.py文件设置
# 指定日志的级别
#LOG_LEVEL = "WARNING"
# 将日志打印出到指定文佳佳
LOG_FILE = "./logdemo.log"
1.重写start_requests方法:
def start_requests(self)
2. start_requests的返回值
scrapy.FormRequest(url,headers,callback,formdata)
url:发送的post地址
headers:可以定制头信息
callback:回调函数
formdata:post所携带的数据,这是一个字典
import scrapy
class PostSpider(scrapy.Spider):
name = "post"
allowed_domains = ["fanyi.baidu.com"]
# post请求,如果没有参数,那么这个请求就没有任意
# 因此start_urls没有用了——响应的parse方法也没用了
"""
start_urls = ["http://fanyi.baidu.com/"]
def parse(self, response):
pass
"""
# post请求
def start_requests(self):
url = "https://fanyi.baidu.com/sug"
data = {
"kw":"final"
}
yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse_second)
def parse_second(self, response):
import json
print(json.loads(response.text))
(1) 到settings.py打开一个选项
DOWNLOADER_MIDDLEWARES = {
'postproject.middlewares.Proxy': 543,
}
(2)到middlewares.py写代码
def process_request(self, request, spider):
request.meta['proxy'] = 'https://113.68.202.10:9999'
return None