scrapy startproject mySpider
2、新建一个爬虫
scrapy genspiders spiders
import scrapy
class SpidersSpider(scrapy.Spider):
name = 'spiders' # 爬虫名
allowed_domains = ['itcast.cn'] # 允许爬虫的范围
start_urls = ['http://itcast.cn/'] # 最开始请求的url的地址
def parse(self, response):
# 处理start_urls 地址对应的响应
li_list = response.xpath('//div[@class="tea_con"]')
for li in li_list:
item = {
}
item['name'] = li.xpath(".//h3/text()").extract_first()
item['title'] = li.xpath(".//h4/text()").extract_first()
# res = response.xpath('//div[@class="tea_con"]//h3/text()').extract_first()
# print(res)
yield item # 把列表传到piplines中
注:xpath写错会默认给提供None值
3、启动爬虫
scrapy crwal spiders
在/settings下的设置里面
LOG_LEVEL = "WARNING"
控制台只显示warning以上水平的信息
4、pipline[管道]处理
/parse()下:
yield item # 把列表传到piplines中
首先:settings中把注释的piplines取消注释
/settings下
ITEM_PIPELINES = {
'myspider.pipelines.MyspiderPipeline': 300, # 数据越小,越先执行
'myspider.pipelines.MyspiderPipeline1': 301, # 数据越小,越先执行
}
/piplines下
定义两个pipline类
class MyspiderPipeline:
def process_item(self, item, spider):
print(item)
item["hello"] = 'word'
return item
class MyspiderPipeline1:
def process_item(self, item, spider):
print(item)
return item
执行结果:
{
'name': '黄老师', 'title': '高级讲师'}
{
'name': '黄老师', 'title': '高级讲师', 'hello': 'word'}
5、如何区别多个爬虫的pipline
方式一:
def parse(self, response):
item = {
}
item["come_from"] = 'itcast'
class MyspiderPipeline:
def process_item(self, item, spider):
if item['come_from'] == 'itcast':
print(item)
item["hello"] = 'word'
return item
方式二【推荐】:
class MyspiderPipeline:
def process_item(self, item, spider):
if spider.name == 'itcast':
6、logging输出
import logging
logger = logging.getLogger(__name__) # 能输出当前日志的输出
/setting下
LOG_LEVEL = "WARNING"
LOG_FILE = "./log.log" # 将日志保存到本地
7、logging 非scrapy 输出
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(levelname)s %(filename)s '
'%(message)s'
' - %(asctime)s', datefmt='[%d/%b/%Y %H:%M:%S]',
filename='./loggmsg.log', filemode="a")
logger = logging.getLogger(__name__)
8、实现翻页
next_page_url = response.xpath("//a[text()='下一页']/@href).extract()
while len(next_page_url)>0:
yield scrapy.Request(next_page_url, callback=self.parse)
#9、yield scrapy.Request()使用介绍
yield scrapy.Request(url,
callback, # 指定传入的url交给哪个解析函数去处理
method='POST',
headers,
body,
cookies,
meta, # 实现在不同的解析函数中传递数据,meta默认会携带部分信息
dont_filter=False,
)
上注:
meta:实现在不同的解析函数中传递数据,meta默认会携带部分信息
例:
yield scrapy.Request(next_page_url, callback=self.parse1
meta = {
'item': item}
)
def paras1(self, response)
response.meta['item'] #在此取到上面出来的item
dont_filter=False: 让scrapy的去重不会过滤当前url,scrapy默认有url去重功能,对需要重复请求的url有重要用途
列表表达式:
item = ['http://url'+i for i in item['url']
piplines.py 下
def process_conent(self, content):
content = [re.sub(r"\xa0|\s|\t","", i) for i in content]
content = [i for i in content if len(i)>0]
# 去除列表中的字符串
scrapy shell https://www.baidu.com
可以进一步查看请求和响应信息
response.url: 当前响应的网址
response.request.url: 当前响应对应请求的url地址
response.headers: 请求头
response.body: 响应体
response.request.headers:当前响应的请求头
# Configure maximum concurrent requests performed by Scrapy (default: 16) 最大并发请求
#CONCURRENT_REQUESTS = 32
#下载延迟,每次下载等三秒
#DOWNLOAD_DELAY = 3
# Disable cookies (enabled by default)
# 是否开启cookie,默认情况是开启的。
# COOKIES_ENABLED = False
# Override the default request headers:默认请求头
# DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
#自动限速
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
# AUTOTHROTTLE_ENABLED = True
http缓存
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
# HTTPCACHE_ENABLED = True
# HTTPCACHE_EXPIRATION_SECS = 0
# HTTPCACHE_DIR = 'httpcache'
# HTTPCACHE_IGNORE_HTTP_CODES = []
# HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
主程序中使用settings中的内容:
self.settings.get('HTTPCACHE_ENABLED ')
import json
class JsonWriterPipline(object):
#在爬虫开启的时候,仅执行一次
def open_spider(self, spider):
self.file = open(spider.settings.get("SAVE_FILE","./item.json"),"w")
#在爬虫结束的时候,仅执行一次
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(dict(item))+ "\n"
self.file.wirte(line)
return item
#不return的情况下,另一个权重较低的pipline就不会获取到该item
# Obey robots.txt rules 不遵循网站的机器规则
ROBOTSTXT_OBEY = False
# Override the default request headers: 设置默认请求头
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 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
}
class QsbkPipeline:
def __init__(self):
self.fp = open('duanzi.json', 'w', encoding='utf-8')
# 爬虫开始前调用open_spider
def open_sipder(self, spider):
print('这是爬虫开始。。')
def process_item(self, item, spider):
item_json = json.dumps(item, ensure_ascii=False)
self.fp.write(item_json + '\n')
return item
# 爬虫结束前调用close_spider
def close_spider(self, spider):
print('这是爬虫结束。。')
注意:settings中打开
ITEM_PIPELINES = {
'qsbk.pipelines.QsbkPipeline': 300,
}
items下:定义两个字段
import scrapy
class QsbkItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()
spider下:
from items import QsbkItem
text_dict = QsbkItem(author=author, content=content)
yield text_dict
piplins下:
item_json = json.dumps(dict(item), ensure_ascii=False)
self.fp.write(item_json + '\n')
return item
笔记:
1. response是一个'scrapy,http.response.html.HtmlResponse'对象,可直接执行'xapth', 'css'语法来提取数据。
2. 提取出来的数据,是一个Selector或者是一个SelectorLIst对象。想要获取其中的字符串,那么应该执行"get()"或者'getall()'方法。
3. getall()方法:提出'Selcetor'中的所有文本,返回的是一个列表。
4. get()放啊:提出'Selcetor'中第一个文本,返回的是一个str字符串。
5. 如果数据解析回来,要传给pipeline处理,那么可以使用yield来返回,或者是提前定义一个空列表,最后return 列表。
6. item:“建议在‘items.py’中定义好模型。
7.pipeline:这是一个专门用来保存数据的,其中有三个方法会被经常用到。
'open_spider(self, spider)':当爬虫打开时运行
'process_spider(self, spider)':当爬虫有item传过来的时候被调用。
'close_spider(self, spider)':当爬虫结束时被调用。
要激活pipeline:在'settings.py'中打开中间件。
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline:
def __init__(self):
self.fp = open('duanzi.json', 'wb')
self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii=False, encoding='utf-8')
# 爬虫开始前调用open_spider
def open_sipder(self, spider):
print('这是爬虫开始。。')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
# 爬虫结束前调用close_spider
def close_spider(self, spider):
self.fp.close()
print('这是爬虫结束。。')
笔记:
'Jsonitemexporter' 和'jsonLinesitemexporter'
保存json数据的时候,可以使用这两个类,让操作变得简单
1.'Jsonitemexporter':每次把数据添加到内存当中,最后统一写入到磁盘中,好处是:存储的数据是一个满足就送规则的数据,坏处是数据量比较大,比较耗内存。
2、'jsonLinesitemexporter':这个是每次调用'export_item'的时候就把这个item存储到硬盘中,坏处是每一个字典是一行,整个问价你不是一个满足json格式的文件,好处是每次处理的数据的时候就直接存储到了硬盘当中,这样不会消耗内存,数据比较安全。
next_url = response.xpath('//ul[@class="pagination"]/li[last()]/a/@href').get()
if not next_url:
return
else:
print('https://www.qiushibaike.com/'+next_url)
yield scrapy.Request('https://www.qiushibaike.com/'+next_url, callback=self.parse)
request对象在我们写爬虫时发送一个请求的时候调用,常用参数有:
1. 'url': url地址
2. 'callback': 在下载器下载完成相应数据后执行的回调函数
3. 'method':请求的方法,默认为get
4. 'headers':请求头,一些固定的设置,放在;'setting.py'中制定就可以了,对于非固定的,在发送请求时指定。
5. 'meta': 比较常用用于在不同的请求之间传递数据用。
6. 'encoding':编码,默认为utf-8
7. 'dot_filter':表示不由调度器过滤,在执行多次重复的请求时用的较多。如过之前发送过这个链接请求,调度器默认会拒绝再次发送该请求,所以要设置成Flase
8. 'errback':发成错误时候执行的函数
'Post'请求:Requset子类-> FromRequest来实现
1.'meta':从其他请求传过来的meta属性,可以用来保持多个请求之间的数据连接。
2.encoding:返回当前字符串编码和届满的格式
3.'text': 将返回的数据作为Unicode字符串返回。
4.'body':将返回来额数据作为bytes字符串返回。
5.'xpath':xpath选择器。
6.'css':css选择器。
'Post'请求:Requset子类-> FromRequest来实现,如果在爬虫一开始时候就发送post请求,那么需要再爬虫类中重写 start_requset(self)方法,并且不再 调用start_requset(self)中的url。
class RenrenSpider(scrapy.Spider):
name = 'renren'
allowed_domains = ['renren.com']
start_urls = ['http://renren.com']
def start_requests(self):
url = 'http://www.renren.com/PLogin.do'
data = {
'email': '', 'password': ''}
requset = scrapy.FormRequest(
url,
formdata=data,
callback=self.parse_page
)
# 将执行requset
yield requset
def parse_page(self, response):
# 访问一下界面【只有登录成功】
resquset = scrapy.Request(
url='http://www.renren.com/123456/profile',
callback=self.parse_profile,
)
yield resquset
def parse_profile(self, response):
with open('dp.html', 'wb', encoding='utf-8') as f:
f.write(response.text)
import scrapy
from PIL import Image
from urllib import request
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['douban.com']
start_urls = ['https://accounts.douban.com/login']
profile_url = 'https//www.douban.com/people/123456'
def parse(self, response):
formdata = {
'source': 'None',
'redir': 'https://www.douban.com',
'from_email': '',
'from_pasword': '',
'login': '登录',
}
captcha_url = response.css('img#captcha_image::attr(src)').get()
# 如果没有验证码
if captcha_url:
captcha = self.regonize_captcha(captcha_url)
formdata['captcha-solution'] = captcha
captcha_id = response.xpath('//input[@name="captcha_id"]/@value')
formdata['captcha_id'] = captcha_id
yield scrapy.FormRequest(
url='',
formdata=formdata,
callback=self.parase_after_login
)
def regonize_captcha(self, image_url):
# 下载验证码的图片
request.urlretrieve(image_url, 'capatcha.png')
image = Image.open('capatcha.png')
image.show()
captcha = input('请输入验证码:')
return captcha
def parase_after_login(self, response):
# 判断是否登录成功
if response.url == 'https//www.douban.com':
yield scrapy.Request(
url=self.profile_url,
callback=self.parase_profile
)
print('登陆成功')
else:
print('登录失败')
def parase_profile(self, response):
print(response.url)
if response.url == self.profile_url:
ck = response.xpath('//input[@name="ck"]/@value')
fromdata ={
'ck': ck,
'signatures': '则是修改的个人签名'
}
print('成功进入该界面')
else:
print('没有进入到个人中心')
注意:如果最后一个方法没有给callback,则会自动去执行parse()方法,造成多余回调
1.避免重新下线最近已经下载过的数据。
2.可以方便的指定文件存储的路径。
3.可以将下裁的图片转换成通用的格式。比如png或pg。
4.可以方便的生成睿备图。
5.可以方便的检测图片的宽和高,确保他们满足最小限制。
6.异步下载,效率非常高。
当使用Files Pipeline下戏文件的时候,按照以下步强来完成:
1.定义好一个Iten ,然后在这个iten中定义两个属性,分别为
File url以及filcs 。file_urls是用来存储需要下裁的文件的
ur链接,需要给一个列表。
2.当文件下或完成后,会把文件下载的相关信息存储到iten的 files 属性中。比如下或路径、下觌的uri和文件的校猃
3.在配用文件 settings.py 中配嚣FILEs_STORE ,这个配用是用来设用文件下赖下来的路径。
4.启动pipeline:在ITEN_PIPELINES 中设置scrapy.pipelines.files. FilesPipcline:1 .
当使用Images Pipeline下锐文件的时候,按照以下步骏来完成:
1.定义好一个 Item ,然后在这个iten 中定义两个属性,分别为 inago _un1s 以及 imsges . inage_ur1s 是用来存储需要下赖的图片的url链接,需要给一个列表。
2.当文件下戟完成后,会把文件下载的相关信息存储到item的imsges属性中。比扣下载路径、下觌的url和图片的核金码等。
3.在配置文件 settings.py中配置1HAGEs_STORE ,这个配置是用来设置图片下载下来的路径。
4.启动pipeline :在 ITEN_PIPELINES 中设置scrapy.pioelines.images.ImagesPipeline
下载器中间件我们可以设置代理,更换请求头来达成反反爬虫的目的。
要写下载器中间件,可以在下载其中实现两个方法,一个是process_request(self,requset,spider),这个方法会在请求发送之前执行。还有一个是'process_response(selfm response, spider)'在数据下载到引擎之前执行。
/middlerwares.py下
class UserAgentDOwnloadMiddleware(object):
user_agent = [
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0",
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10",
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
"Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+",
"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0",
"Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)",
"UCWEB7.0.2.37/28/999",
"NOKIA5700/ UCWEB7.0.2.37/28/999",
"Openwave/ UCWEB7.0.2.37/28/999",
"Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999",
# iPhone 6:
"Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25",
def process_request(self, requeset, spider):
user_agent = random.choice(user_agents)
requset.heasers['User_Agent'] = user-agent
/middlewares.py
class IPProxyDownloadMiddleware(object):
def ___init(self):
proxys = ['127.0.0.1:800']
def process_request(self, request, spider):
proxy = random.choice(self.proxys )
request.meta['proxy'] = proxy
/middlewares.py
class IPProxyDownloadMiddleware(object):
def process_request(self, request, spider):
proxy = '121.1999.6.124:16816'
user_password = 'username:password'
request.meta['proxy'] = proxy
b64_user_pwd = base64.b64encode(user_password.encode('urf-8'))
request.headers['Proxy-Authorization'] = 'Basic '+ b64_user_pwd