全国旗舰校区

不同学习城市 同样授课品质

北京

深圳

上海

广州

郑州

大连

武汉

成都

西安

杭州

青岛

重庆

长沙

哈尔滨

南京

太原

沈阳

合肥

贵阳

济南

下一个校区
就在你家门口
+
当前位置:首页  >  技术干货

20天学会爬虫之Scrapy框架Spider类(案例:披荆斩棘的哥哥)

发布时间:2022-09-19 17:59:40
发布人:qyf

  Spider是什么?

  • Spider是一个Scrapy提供的基本类,Scrapy中包含的其他基本类(例如CrawlSpider)以及自定义的spider都必须继承这个类。

  • Spider是定义如何抓取某个网站的类,包括如何执行抓取以及如何从其网页中提取结构化数据。

  源码如下:

  所有爬虫的基类,用户定义的爬虫必须从这个类继承

  class Spider(object_ref):

  #name是spider最重要的属性,而且是必须的。一般做法是以该网站(domain)(加或不加 后缀 )来命名spider。 例如,如果spider爬取 mywebsite.com ,该spider通常会被命名为 mywebsite

  name = None

  #初始化,提取爬虫名字,start_ruls

  def __init__(self, name=None, **kwargs):

  #判断是否存在爬虫名字name,没有则会报错

  if name is not None:

  self.name = name

  elif not getattr(self, 'name', None):

  raise ValueError("%s must have a name" % type(self).__name__)

  # python对象或类型通过内置成员__dict__来存储成员信息

  self.__dict__.update(kwargs)

  #判断是否存在start_urls列表,从列表中获取到页面的URL开始请求,后续的URL将会从获取到的数据中提取。

  if not hasattr(self, 'start_urls'):

  self.start_urls = []

  # Scrapy执行后的日志信息

  def log(self, message, level=log.DEBUG, **kw):

  log.msg(message, spider=self, level=level, **kw)

  # 判断对象object的属性是否存在,不存在则做断言处理

  def set_crawler(self, crawler):

  assert not hasattr(self, '_crawler'), "Spider already bounded to %s" % crawler

  self._crawler = crawler

  @property

  def crawler(self):

  assert hasattr(self, '_crawler'), "Spider not bounded to any crawler"

  return self._crawler

  @property

  def settings(self):

  return self.crawler.settings

  #该方法将读取start_urls内的地址,并为每一个地址生成一个Request对象,交给Scrapy下载并返回Response

  #注意:该方法仅调用一次

  def start_requests(self):

  for url in self.start_urls:

  # 生成Request对象的函数

  yield self.make_requests_from_url(url)

  #Request对象默认的回调函数为parse(),提交的方式为get

  def make_requests_from_url(self, url):

  return Request(url, dont_filter=True)

  #默认的Request对象回调函数,处理返回的response。

  #生成Item或者Request对象。用户需要自己重写该方法中的内容

  def parse(self, response):

  raise NotImplementedError

  @classmethod

  def handles_request(cls, request):

  return url_is_from_spider(request.url, cls)

  def __str__(self):

  return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))

      __repr__ = __str__

  因此可以总结出Scrapy爬取数据的过程如下:

  Spider的入口方法(start_requests())请求start_urls列表中定义的url,返回Request对象(同时默认传给它一个名为parse的回调函数)。

  下载器获取Respose后,回调函数会解析Reponse,返回(yield)的结果可能是字典、Item或是Request对象,亦或是这些对象组成的可迭代类型。其中,返回的Request也会包含一个回调函数,并在被下载之后被回调函数处理(即重复第2步)。

  解析数据可以使用Scrapy自带的Selectors工具或者lxml、BeautifulSoup等模块。

  最后Scrapy将返回的数据字典(或是Item对象)保存为文件或者保存在数据库中。

  scrapy.spider.Spider类介绍

  常用类属性

  • name:是字符串。标识了每一个spider的名字,必须定义且唯一。实际中我们一般为每个独立网站创建一个spider。

  • starturl:是包含初始请求页面url的列表,必须定义。`startrequests()方法会引用该属性,发出初始的Request`。

  • custom_settings:是一个字典,每一条键值对表示一个配置,可用于覆写SETTINGS(Scrapy的全局配置模块,位于settings.py文件中)。

  •

  – 例1:custom_settings = {'COOKIES_ENABLED': True,'ROBOTSTXT_OBEY': False}。覆盖了全局属性COOKIES_ENABLED。

  – 扩展:设置settings中的值的几种方法,优先级从高到低如下:

  命令行选项

  custom_settings

  settings.py文件

  命令行的默认设置,每一个命令行都有它自己的默认设置

  默认的全局设置,被定义在 scrapy.settings.default_settings 中

  • allowed_domains:是一个字符串列表。规定了允许爬取的网站域名,非域名下的网页将被自动过滤。

  •

  – 例1:allowed_domains = cnblogs.com,start_url = 'https://www.zhihu.com'。在这个例子中,知乎不属于CSDN的域名,因此爬取过程中会被过滤。

  • crawler:是一个Crawler对象。可以通过它访问Scrapy的一些组件(例如:extensions, middlewares, settings)。

  •

  – 例1:spider.crawler.settings.getbool('xxx')。这个例子中我们通过crawler访问到了全局属性。

  • settings:是一个Settings对象。它包含运行中时的Spider的配置。这和我们使用spider.crawler.settings访问是一样的。

  • logger:是一个Logger对象。根据Spider的name创建的,它记录了事件日志。

  常用方法

  • start_requests:该方法是Spider的入口方法。默认下,该方法会请求start_url中定义的url,返回对应的Request,如果该方法被重写,可以返回包含Request(作为第一个请求)的可迭代对象或者是FormRequest对象,一般POST请求重写该方法。

  • parse:当其他的Request没有指定回调函数时,用于处理下载响应的默认回调,主要作用:负责解析返回的网页数据(response.body),提取结构化数据(生成item)生成需要下一页的URL请求。。该方法用于编写解析网页的具体逻辑(包含解析数据,或是解析出新的页面),所以此方法非常重要哦!。

  Spider案例:披荆斩棘的哥哥评论

  最近被披荆斩棘的哥哥所吸引,但是还是要为大家做好服务,每天更新文章啊!介绍下这个综艺节目哈。

  《披荆斩棘的哥哥》是芒果TV推出的全景音乐竞演综艺。节目嘉宾们彼此挑战,披荆斩棘,通过男人之间的彼此探索、家族建立的进程,诠释“滚烫的人生永远发光”,见证永不陨落的精神力。

  我们本次使用Scrapy爬取哥哥们的评论。

Picture

  分析思路:

  打开谷歌浏览器,访问第01期的链接(https://www.mgtv.com/b/367750/13107580.html),把JavaScript加载关掉,刷新,发现底下的评论数据没有了,说明这数据是异步加载的,在这个网页链接的源代码里是找不到评论数据的;

  既然是异步加载,那么就要抓包了。把刚刚关掉的JavaScript打开,重新加载网页,右键检查,Network, 数据一般都在XHR或者JS里面,所以先把这两项勾选了,这时候点击评论的下一页,发现数据就在JS里面:

Picture(1)

Picture(2)

  由上面评论的真实链接可以知道,评论真实的请求网址是:“https://comment.mgtv.com/v4/comment/getCommentList?”,后面跟着一系列的参数(callback, _support, subjectType, subjectId, page, _),可见:

Picture(3)

  我们知道page是页码数,subjectId是s每个视频对应的id,callback回调函数,最后一个大胆猜测下就是unix时间戳后面再加上3位随机数(或者unix时间戳乘以1000再取整),应该只起一个占位的作用,可能是一个完全没用的参数,只是用来吓唬我们的。

  但是不确定,我们来看一下,于是我去掉最后一个参数在浏览器发出了一下请求,结果如下:

Picture(4)

  说明就是一个完全没用的参数,哈哈哈用来吓唬我们的,不要怕!我们不用它。

  链接有了之后我们就开始创建爬虫项目啦!

  首先打开命令行,输入:

  scrapy startproject mongotv_comments_crawler

  生成新的mongotvcommentscrawler项目,再输入:

  cd mongotv_comments_crawler

  scrapy genspider mgtv_crawl mgtv.com

  生成爬虫名。

  然后,用PyCharm打开项目。由于最后爬取到的是json数据,我们直接解析Json数据,并返回到Items中。

  因此在爬虫文件mgtv_crawl.py的MgtvCrawlSpider类中,进行如下定义:

  class MgtvCrawlSpider(scrapy.Spider):

  name = 'mgtv_crawl'

  allowed_domains = ['mgtv.com']

  # start_urls = ['http://mgtv.com/'] 因为我们每次都需要构建芒果TV的请求,所以我们重写start_requests方法

  subject_id = 4327535 # 视频的id

  pages = list(range(1, 100)) # 需要爬取的评论页数比如100页

  因为我们要爬取多页的内容,所以我们要不断修改page参数,所以我们重写start_requests方法

  def start_requests(self): # 重写start_requests

  start_urls = [f'https://comment.mgtv.com/v4/comment/getCommentList?page={page}&subjectType=hunantv2014&subjectId={self.subject_id}&callback=jQuery18204988030991528978_1630030396693&_support=10000000&_=1630030399968' for page in self.pages]

  # 生成所有需要爬取的url保存进start_urls

  for url in start_urls: # 遍历start_urls发出请求

  yield Request(url)

  然后重写parse()函数,获取json结果。但是json结果前面有下图一样的前缀内容,我们要去掉

Picture(5)

  def parse(self, response):

  text = response.text[response.text.find('{'):-1] # 通过字符串选取的方式把"jQuery...()去掉"

  json_data = json.loads(text) # 转换成json格式

  for i in json_data['data']['list']: # 遍历每页的评论列表

  item = MongotvCommentsCrawlerItem()

  item['content'] = i['content']

  item['commentId'] = i['commentId']

  item['createTime'] = i['createTime']

  item['nickName'] = i['user']['nickName']

  yield item

  编写item,获取评论的:内容、创建时间、用户名和评论ID

  class MongotvCommentsCrawlerItem(scrapy.Item):

  # define the fields for your item here like:

  # name = scrapy.Field()

  content = scrapy.Field()

  createTime = scrapy.Field()

  nickName = scrapy.Field()

  commentId = scrapy.Field()

  然后便是写pipelines.py文件,把爬取回来的items入库

  import pymysql

  class MongotvCommentsCrawlerPipeline(object):

  def __init__(self):

  self.conn = pymysql.connect(host='127.0.0.1', user='root', password='root',

  db='mgtv', charset='utf8')

  def process_item(self, item, spider):

  commentId = item["commentId"]

  content = item['content']

  createTime = item['createTime']

  nickName = item["nickName"]

  sql = "insert into comments(commentId,content,createTime,nickName) values(" + str(commentId) + ",'" + content + "','" + createTime + "','" + nickName + "');"

  self.conn.query(sql)

  self.conn.commit()

  return item

  def close_spider(self, spider):

  self.conn.close()

  在settings.py中开启对应的设置项:

Picture(6)

Picture(7)

Picture(8)

  开启爬虫进行爬取:

  scrapy crawl mgtv_crawl

  爬取到的结果如下:

Picture(9)

相关文章

Visual Studio Online和GitHub有什么区别?

Visual Studio Online和GitHub有什么区别?

2023-10-15
计算机视觉中所指的深度和深度学习中的深度有什么区别?

计算机视觉中所指的深度和深度学习中的深度有什么区别?

2023-10-15
显著性目标检测和一般目标检测最本质的区别是什么区别?

显著性目标检测和一般目标检测最本质的区别是什么区别?

2023-10-15
在目标检测里single-shot和multi-shot的主要区别是什么?

在目标检测里single-shot和multi-shot的主要区别是什么?

2023-10-15

最新文章

常见网络安全面试题:Windows常用的命令有哪些?

常见网络安全面试题:Windows常用的命令有哪些?

2023-10-09
常见网络安全面试题:根据设备告警如何展开排查?

常见网络安全面试题:根据设备告警如何展开排查?

2023-10-09
常见网络安全面试题:mysql加固呢?(数据库加固)

常见网络安全面试题:mysql加固呢?(数据库加固)

2023-10-09
常见网络安全面试题:windows和linux加固?(操作系统加固)

常见网络安全面试题:windows和linux加固?(操作系统加固)

2023-10-09
在线咨询 免费试学 教程领取