赞
踩
学习scrapy前, 我们需要看懂下图
请务必仔细阅读并理解该流程:
0- spider优先提供一个起始url(start_url)和想要爬取的域名, 发送给引擎。
1- scrapy框架核心为引擎(ScrapyEngine)。启动scrapy时。引擎会读取spiders中由start_url构建出的request请求。
2- Engine 将封装好的request推送到 Scheduler调度器中 。调度器使用过滤器读取setting中的配置构成请求队列返回给引擎。
3- 引擎按照 Scheduler中的安排下发任务给 Downloader下载器 下载器请求Internet去获取相应的响应
4- 其中Requests和Response都会经过DownloadMiddlerwares,一些特殊请求或响应可以在中间件中构建或处理完成 。
5- 引擎获取到Downloader中返回的Response后,将response传递给spider。 spider通过parse方法对response进行解析
6- 获取到解析数据后,spider将数据返回给引擎。 如果还有回调函数。spider将url重新封装成Request再次返回给引擎
7- 引擎将所有的Request调用完毕, 结束流程
总结
scrapy是一个自动化的框架, 它给我们提供了 Spiders, Engine, Scheduler, Downloader,
Pipelines 以及 Middleware功能。-其中我们只用编写或修改部分功能。 如
1- Spiders功能中, 我们需要编写 【提取和构建 requests & Response 数据处理 】
2- setting中配置Scheduler需要的参数
3- DownloaderMiddleware中构建请求的特殊参数。 如(UA, IP池等)
4- Pipline中做结果处理操作。如(数据入库)
note: 我们所有的步骤全部又Engine调度
1- python 解释器
2- pycharm 编辑工具
3- scarpy 安装scrapy包
note: 这里有个坑。 我们正常安装的的scrapy版本与scrappy-redis可能会有冲突。所以我们通常会固定scrapy版本。如果以后要使用scrapy-redis的话, 建议按照如下操作更新scrapy。
执行一下步骤:
pip uninstall scrapy 输入:y确认卸载
pip install scrapy2.5.1 安装指定的scrapy版本
scrapy version --verbose 查看scrapy中各个工具的版本。
pip uninstall cryptography 如果cryptography版本不在 1.. 则卸载 cryptography
pip install cryptography36.0.2 固定版本36.0.2
至此。 scrapy版本即固定成功
一切准备就绪,我们开始创建自己的第一个scrapy爬虫
scrapy startproject ant_spider
cd ant_spider
scrapy genspider yanzhaopro https://yz.chsi.com.cn
此时我们指定了要爬取 https://yz.chsi.com.cn 下的内容
![在这里插入图片描述](https://img-blog.csdnimg.cn/6baa8ba8d916448681633a1c31fb51e0.png)
此时我们的scrapy项目创建成功
1, scrapy是一个爬虫框架, 即我们只需要做出相应的配置, 它就能按照指定的步骤去执行爬虫步骤。
首先我们看下setting中的配置:
1- 我们当然不应该遵守 Robots 协议 ROBOTSTXT_OBEY = False note: 如果不清楚robots协议还是要去百度一下, 简单来说就是互联网中的君子协议。 如果遵守就没必要学习爬虫了。 2- 请求并发量 CONCURRENT_REQUESTS = 32 note: 如果目标网站数据不敏感当然可以高一点(可以达到200。其实可以更高, 鉴于数据不敏感的网站服务器一般不会太牛, 200差不多了, 再高可能会影响爬虫的成功率)。 但是目标网站过于敏感还是建议使用低并发处理(50以下) 3- 下载延时 DOWNLOAD_DELAY = 3 note: 这个参数可以高一点。 我们请求成功就行, 如果因为下载太多频繁导致成功率太低得不偿失(3-10之间都可以) 4- 域名和ip的并发量 CONCURRENT_REQUESTS_PER_DOMAIN = 16 CONCURRENT_REQUESTS_PER_IP = 16 note: 这两个是配套使用, 具体用法可以百度。 参数调节一般二者选一个就行。值尽量在20-50之间 5- cookie的使用 COOKIES_ENABLED = False note: 这里其实也是大有学问, 本质就是是否使用scrapy配置的cookie, 或者手动添加cookie。 值为False: 不会自动添加cookie, 如果需要用cookie,则手动添加 值不为False(为True或者注释掉): 自动添加cookie 6- 开启控制台调试打印 TELNETCONSOLE_ENABLED = True 7- 给定默认请求头 DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', } 8- 开启middleware SPIDER_MIDDLEWARES = { 'ant_spiderpro.middlewares.AntSpiderproSpiderMiddleware': 543, } DOWNLOADER_MIDDLEWARES = { 'ant_spiderpro.middlewares.AntSpiderproDownloaderMiddleware': 543, } 9- item操作 ITEM_PIPELINES = { 'ant_spiderpro.pipelines.AntSpiderproPipeline': 300, } 10- 设置日志等级 LOG_LEVEL = "WARNING"
到这一步我们就可以正式编写一个scrapy了
还记得第一步吗, spider中构建一个request请求。
找到spider
编写spider代码如下
import scrapy from scrapy.http import Request class YanzhaoproSpider(scrapy.Spider): name = 'yanzhaopro' def __init__(self): # 需要爬取的页面, 这里可以添加url池 self.start_urls = ['http://yz.chsi.com.cn/'] def start_requests(self): if not hasattr(self, 'start_urls'): self.start_urls = [] for url in self.start_urls: print("我们开始构建请求") yield Request(url, dont_filter=True) def parse(self, response): print("我们对response进行处理") print(response.text)
此时有几种方式启动spider, 我这里只说一种。
在项目中创建一个 start_spider 的py文件
from scrapy import cmdline
cmdline.execute(['scrapy', 'crawl', 'yanzhaopro'])
执行既能启动
1- setting中的初级配置
BOT_NAME = 'firstcrawl' SPIDER_MODULES = ['firstcrawl.spiders'] NEWSPIDER_MODULE = 'firstcrawl.spiders' # 设置日志等级 LOG_LEVEL = "WARNING" # Crawl responsibly by identifying yourself (and your website) on the user-agent USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36' # Obey robots.txt rules ROBOTSTXT_OBEY = False # Configure maximum concurrent requests performed by Scrapy (default: 16) # scrapy默认是协程任务 CONCURRENT_REQUESTS = 32 # Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { # 管道: 优先级 'firstcrawl.pipelines.FirstcrawlPipeline': 300, }
2- spider中的代码
import scrapy class XiaoyouxiSpider(scrapy.Spider): name = 'xiaoyouxi' # 爬虫的名字 allowed_domains = ['4399.com'] # 爬虫允许访问的域名是什么 start_urls = ['https://www.4399.com/flash/'] # 起始 url def parse(self, response): print(response.text) print("================================================================") # scrapy 中封装了lxml, 可以直接使用 .xpath 方法 li_list = response.xpath("//ul[@class='n-game cf']/li") for li in li_list: # scrapy中的xpath不是调用的 lxml 包中的xpath。 # 框架中的xpath提取的是一个选择器(Selector)。 从选择器中提取数据需要使用: extract() / extract_first() # extract() 为提取全部 # extract_first() 为提取第一个 game_name = li.xpath("./a/b/text()").extract_first() game_type = li.xpath("./em/a/text()").extract_first() game_img = "https:" + li.xpath("./a/img/@lz_src").extract_first() game_time = li.xpath("./em[last()]/text()").extract_first() # 返回数据而不打断程序的执行 # ERROR: Spider must return request, item, or None yield { "game_name": game_name, "game_type": game_type, "game_img": game_img, "game_time": game_time, }
note:其中特别说明的是 scrapy 中使用xpath不是基于 lxml 库。 所以用法上有点区别
为了解决xpath提取文本时以索引获取数据可能报错的问题
如果使用[0]索引取值。 页面中没有该值则会报错【li.xpath(“./a/b/text()”)[0]】
scrapy将xpath提取数据封装成了selector选择器, 当xpath中的对象不存在时。 返回为None, 避免流程无故中断
而从选择器中取值提供了两个方法: 【extract_first()】取第一个值。 【extract()】 提取全部值
3- pipline中的代码
# 管道使用。必须在settings中开启
class FirstcrawlPipeline:
# item 数据
# process 处理
def process_item(self, item, spider):
print("这里是管道: ", item)
with open("data.csv", mode="a", encoding="utf-8") as f:
f.write(f"{item['game_name']}, {item['game_type']}, {item['game_img']}, {item['game_time']}\n")
return item
返回结果如下
以该url: 【https://desk.zol.com.cn/dongman/】 为例。 我们需要获取页面上的img详情链接, 请求详情链接中的图片进行下载
此时步骤为:
…1- 第一次请求获取页面上的img_url的详情地址。
…2- 第二次请求从详情页面中获取多个图片地址。
…3-最后针对每个url进行下载
1- setting.py中的代码
BOT_NAME = 'tupian' SPIDER_MODULES = ['tupian.spiders'] NEWSPIDER_MODULE = 'tupian.spiders' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36' ROBOTSTXT_OBEY = False LOG_LEVEL = "WARNING" ITEM_PIPELINES = { 'tupian.pipelines.DetailTupianPipeline': 301, } IMAGES_STORE = "./jia" MEDIA_ALLOW_REDIRECTS = True
2- spider中的代码
import scrapy from scrapy.http import Request class JiaSpider(scrapy.Spider): name = 'jia' allowed_domains = ['zol.com.cn'] start_urls = ['https://desk.zol.com.cn/dongman/'] def parse(self, response, **kwargs): print(response.url) print("====================================================") hrefs = response.xpath("//ul[@class='pic-list2 clearfix']/li/a/@href").extract() for href in hrefs: # 屏蔽无用链接 if href.endswith(".exe"): continue # 拼接出完整的路径 """ response路径: https://desk.zol.com.cn/dongman/ 如果抓取路径以“/”开头, 则需要与域名拼接成完整路径: https://desk.zol.com.cn/dongman/ /bizhi/9109_111583_2.html ===》 https://desk.zol.com.cn/bizhi/9109_111583_2.html 如果抓取路径不是以“/”开头, 则需要与response中抓取的同级目录进行拼接: https://desk.zol.com.cn/dongman/zhoujielun bizhi/9109_111583_2.html ===》 https://desk.zol.com.cn/dongman/bizhi/9109_111583_2.html """ download_url = response.urljoin(href) print(download_url) yield Request(download_url, callback=self.detail_parse) def detail_parse(self, detail_response, **kwargs): img_url = detail_response.xpath("//img[@id='bigImg']/@src").extract_first() print(img_url) yield { "img_url": img_url }
3- pipline中的代码
from itemadapter import ItemAdapter from scrapy.pipelines.images import ImagesPipeline from scrapy.pipelines.files import FilesPipeline from scrapy import Request class TupianPipeline: def process_item(self, item, spider): return item class DetailTupianPipeline(ImagesPipeline): # 发送下载请求 def get_media_requests(self, item, info): img_url = item['img_url'] print("来", img_url) # 请求对象船只的最佳方案: meta yield Request(img_url, meta={"img_path": img_url}) def file_path(self, request, response=None, info=None, *, item=None): # 需要返回文件路径 img_path = request.meta["img_path"] img_name = img_path.split("/")[-1] return f"save_img/{img_name}" def item_completed(self, results, item, info): # 对下载结果(results)进行判断, 选择操作 return item
结果展示
步骤说明:
1- spider构建出Request后, 获取response响应。
2- spider中的parse方法解析response后提取出img_url详情页面。 构建出二次请求的Request并回调到 detail_parse 进行解析
【yield Request(download_url, callback=self.detail_parse)】
3- detail_parse 解析出详情页中的img图片, 在管道中进行下载
4- 管道的图片下载有三个函数, 分别是【构建下载请求】 【下载路径处理】 【下载结果逻辑】
图片下载完成
1- 只有spider中的代码需要改动
import scrapy from scrapy import Request class FenleiSpider(scrapy.Spider): name = 'fenlei' allowed_domains = ['17k.com'] start_urls = ['https://www.17k.com/all/book/2_0_0_0_0_0_0_0_1.html'] def parse(self, response, **kwargs): book_list = response.xpath("//table/tbody/tr[contains(@class, 'bg')]") for book in book_list: book_type = book.xpath("./td[@class='td2']/a/text()").extract_first() book_name = book.xpath("./td[@class='td3']//a/text()").extract_first() book_chapters = book.xpath("./td[@class='td4']/a/text()").extract_first() book_author = book.xpath("./td[@class='td6']/a/text()").extract_first() yield { "book_type": book_type, "book_name": book_name, "book_chapters": book_chapters, "book_author": book_author } hrefs = response.xpath("//div[@class='page']/a/@href").extract() for href in hrefs: if href.startswith('javascript'): continue next_url = response.urljoin(href) yield Request(next_url, callback=self.parse)
1- setting中代码
BOT_NAME = 'loginpro' SPIDER_MODULES = ['loginpro.spiders'] NEWSPIDER_MODULE = 'loginpro.spiders' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36' ROBOTSTXT_OBEY = False LOG_LEVEL = "WARNING" COOKIES_ENABLED = False DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', # 这里的cookie不可以直接使用, 因为scrapy中有个cookie的中间件会刷新这里的cookie "Cookie": 'GUID=c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf; c_channel=0; c_csc=web; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F09%252F49%252F49%252F99704949.jpg-88x88%253Fv%253D1670250949000%26id%3D99704949%26nickname%3DMrxiong1223%26e%3D1685804209%26s%3D1ed4b1d810f2a640; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2299704949%22%2C%22%24device_id%22%3A%22184e2b4d45529f-056b1f117d50f2-58412b14-2073600-184e2b4d456d6a%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%2C%22first_id%22%3A%22c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf%22%7D; Hm_lvt_9793f42b498361373512340937deb2a0=1670250813,1670720818; __bid_n=184feb8ad3b63e15874207; FEID=v10-7dfe2cb3a52c946f7bbaf83b0d75a37cdf23252d; __xaf_fpstarttimer__=1670720826804; __xaf_ths__={"data":{"0":1,"1":86400,"2":60},"id":"2601b593-4337-4646-99ed-4cf75e8c080c"}; __xaf_thstime__=1670720826807; FPTOKEN=hhpJOar3Pm4F2QF3tBjIjCg9lkLA2F9Mm71+WEXfUwZ1N+pR9tHaRmQnQVmQ2NgQa0bZb2Ntpxuhtqg4Mg0IBjfB07vqq4z/gzR+++P0jmy/02QTrdlsdljF+AU5emWnGGTdobRj0/9YagyuEdK1sY42ZQiSZYCzOjiYxeoCMEaalSB6k2eyTmgIYlGE4IOeq5DjWK4PYfHProC3piqDWRtApvDQzUmJBySmkFyFQgibQ6YgVht01ZpX+1mGa5o2E1I9uASyQZOeUNMHBiRg097fF05z+chXLoTyaC3rxUmCW55cLXooXV1o1rlzDGDw9Br+6eW+Ha3pMBmOS+Xq3aSNqmnldT1z3pQv9YCl0l5dnhqJeeSceqsYElJG9ACppLEYwiQ2EWxmFBI3wo3ZPQ==|UJQeL0Hjl5mZHzA4CKG3sKH7UoEiBkRXqz3XxPbtPcw=|10|195946289e4b100f86abdbadf9399cd6; __xaf_fptokentimer__=1670720826905; Hm_lpvt_9793f42b498361373512340937deb2a0=1670730045' }
或者也可以使用下面的方式将cookie传入request
import scrapy from scrapy import Request class MyloginSpider(scrapy.Spider): name = 'mylogin' allowed_domains = ['17k.com'] start_urls = ['https://user.17k.com/www/bookshelf/'] def start_requests(self): cookie = 'GUID=c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf; c_channel=0; c_csc=web; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F09%252F49%252F49%252F99704949.jpg-88x88%253Fv%253D1670250949000%26id%3D99704949%26nickname%3DMrxiong1223%26e%3D1685804209%26s%3D1ed4b1d810f2a640; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2299704949%22%2C%22%24device_id%22%3A%22184e2b4d45529f-056b1f117d50f2-58412b14-2073600-184e2b4d456d6a%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%2C%22first_id%22%3A%22c2a0dd44-3d59-4b1c-a17f-5f4f1c0e33bf%22%7D; Hm_lvt_9793f42b498361373512340937deb2a0=1670250813,1670720818; __bid_n=184feb8ad3b63e15874207; FEID=v10-7dfe2cb3a52c946f7bbaf83b0d75a37cdf23252d; __xaf_fpstarttimer__=1670720826804; __xaf_ths__={"data":{"0":1,"1":86400,"2":60},"id":"2601b593-4337-4646-99ed-4cf75e8c080c"}; __xaf_thstime__=1670720826807; FPTOKEN=hhpJOar3Pm4F2QF3tBjIjCg9lkLA2F9Mm71+WEXfUwZ1N+pR9tHaRmQnQVmQ2NgQa0bZb2Ntpxuhtqg4Mg0IBjfB07vqq4z/gzR+++P0jmy/02QTrdlsdljF+AU5emWnGGTdobRj0/9YagyuEdK1sY42ZQiSZYCzOjiYxeoCMEaalSB6k2eyTmgIYlGE4IOeq5DjWK4PYfHProC3piqDWRtApvDQzUmJBySmkFyFQgibQ6YgVht01ZpX+1mGa5o2E1I9uASyQZOeUNMHBiRg097fF05z+chXLoTyaC3rxUmCW55cLXooXV1o1rlzDGDw9Br+6eW+Ha3pMBmOS+Xq3aSNqmnldT1z3pQv9YCl0l5dnhqJeeSceqsYElJG9ACppLEYwiQ2EWxmFBI3wo3ZPQ==|UJQeL0Hjl5mZHzA4CKG3sKH7UoEiBkRXqz3XxPbtPcw=|10|195946289e4b100f86abdbadf9399cd6; __xaf_fptokentimer__=1670720826905; Hm_lpvt_9793f42b498361373512340937deb2a0=1670730045' item_dict = {} for item in cookie.split(";"): item = item.strip() k, v = item.split("=", 1) item_dict[k] = v yield Request(self.start_urls[0], cookies=item_dict) def parse(self, response, **kwargs): print(response.text)
将cookie信息更新在setting中, 并关闭cookie中间件中的覆盖。则request会携带cookie请求页面
1- start_spider
from scrapy import cmdline
cmdline.execute("spider crawl novel_17K".split())
2- setting
BOT_NAME = 'mylogin'
SPIDER_MODULES = ['mylogin.spiders']
NEWSPIDER_MODULE = 'mylogin.spiders'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36'
ROBOTSTXT_OBEY = False
LOG_LEVEL = "WARNING"
DOWNLOAD_DELAY = 1
3- novel_17k
import scrapy from scrapy import Request class Novel17kSpider(scrapy.Spider): name = 'novel_17K' allowed_domains = ['17k.com'] start_urls = ['https://user.17k.com/ck/author/shelf?page=1&appKey=2406394919'] def start_requests(self): login_url = 'https://passport.17k.com/ck/user/login' params = { "loginName": "13059155810", "password": "123456" } """ params_str = "" for k, v in params.items(): params_str += k+"="+v+"&" params_str = params_str[:-1] """ from urllib.parse import urlencode params_str = urlencode(params) yield Request(login_url, method='POST', body=params_str, callback=self.login_success) def login_success(self, response, **kwargs): print(response.text) print("登录成功") yield Request(self.start_urls[0], callback=self.parse) def parse(self, response, **kwargs): print("进入解析页面") print(response.text)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。