Python爬虫第十一课:Scrapy框架(2)——存储数据

在这里插入图片描述
在Scrapy的整个架构中,引擎是可以说有着最高的权利,管理着调度器、下载器、爬虫和数据管道四个重要的组成部分。

这四个组成部分都听命于引擎,一丝不苟的执行引擎下发的命令。
在这里插入图片描述
本次,我们使用Scrapy爬取职友集的招聘信息,让我们更加熟练的掌握Scrapy的用法。

一、明确目标

职友集可以通过索引的方式,搜索到全国上百家招聘网站的最新职位:https://www.jobui.com/rank/company/

我们要获取排行榜中的公司名称、招聘职位名称、工作地点和招聘要求。

二、代码实现

(1)创建项目

在命令行或终端中,cd到想要保存爬虫的目录下,输入创建scrapy项目的命令:scrapy startproject class14(名称可自定义)。

然后在蹦迪电脑的编译器中打开这个scrapy项目,如下所示:
在这里插入图片描述

(2)定义items

在items.py文件中,定义Items:

import scrapy

# 定义一个继承自scrapy.Item的Class14Item类
class Class14Item(scrapy.Item):
    # 定义公司名称的数据属性
    company = scrapy.Field()
    # 定义职位名称的数据属性
    position = scrapy.Field()
    # 定义工作地点的数据属性
    address = scrapy.Field()
    # 定义招聘要求的数据属性
    info = scrapy.Field()

(3)创建和编写爬虫文件

在spiders文件夹下创建爬虫文件,我们将它命名为Jobhui_Info。

import scrapy
import bs4
from ..items import Class14Item

# 定义爬虫类Jobhui_Info
class Jobhui_Info(scrapy.Spider):
    # 定义爬虫的名称
    name = 'Jobhui_Info'
    # 定义允许爬虫爬取网址的域名
    allowed_domains = ['www.jobhui.com']
    # 定义起始网址
    start_urls = ['https://www.jobhui.com/rank/company']

    def parse(self, response):
        # 使用beautifulsoup解析response
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        # 提取1-10的公司,用find_all提取<ul class_="textList flsty cfix">标签
        ul_list = bs.find_all('div',class_='textlist flsty cfix')
        for ul in ul_list:
            a_list = ul.find_all('a')
            for a in a_list:
                # 提取出<a>标签的href属性的值,即公司id标识
                company_id = a['href']
                # 构造出包含公司名称和招聘信息的网址链接的list
                url = 'https://www.jobhui.com{}jobs'.format(company_id)           

接下来,我们要获取不同公司的招聘职位信息。

从这里开始,就需要重新构造新的requests对象和定义新的方法处理response。

import scrapy
import bs4
from ..items import Class14Item

# 定义爬虫类Jobhui_Info
class Jobhui_Info(scrapy.Spider):
    # 定义爬虫的名称
    name = 'Jobhui_Info'
    # 定义允许爬虫爬取网址的域名
    allowed_domains = ['www.jobhui.com']
    # 定义起始网址
    start_urls = ['https://www.jobhui.com/rank/company']

    def parse(self, response):
        # 使用beautifulsoup解析response
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        # 提取1-10的公司,用find_all提取<ul class_="textList flsty cfix">标签
        ul_list = bs.find_all('div',class_='textlist flsty cfix')
        for ul in ul_list:
            a_list = ul.find_all('a')
            for a in a_list:
                # 提取出<a>标签的href属性的值,即公司id标识
                company_id = a['href']
                # 构造出包含公司名称和招聘信息的网址链接的list
                url = 'https://www.jobhui.com{}jobs'.format(company_id)
                # 用yield语句把构造好的request对象传递给引擎。
                # 用scrapy.Request构造request对象。callback参数设置调用parse_Jobhui_Info方法。
                yield scrapy.Request(url, callback=self.parse_Jobhui_Info)

    # 定义新的处理response的方法parse_Jobhui_Info
    def parse_Jobhui_Info(self,response):
        # 解析源码数据
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        # 获取公司名称
        comany = bs.find(class_='company-banner-name')
        # 获取招聘信息的列表
        datas = bs.find_all('div', class_='job-simple-content')
        # 遍历datas
        for data in datas:
            # 实例化Class14Item类
            item = Class14Item()
            # 将公司名称放回Class14Item类的company属性
            item['company'] = comany
            # 提取职位名称,并放回Class14Item类的position属性
            item['position'] = data.find_all('div', class_="job-segmetation")[0].find('h3').text
            # 提取出工作地点,并把这个数据放回Class14Item类的address属性里
            item['address'] = data.find_all('div', class_="job-segmetation")[1].find_all('span')[0].text
            # 提取出招聘要求,并把这个数据放回Class14Item类的detail属性里
            item['info'] = data.find_all('div', class_="job-segmetation")[1].find_all('span')[1].text

            # 用yield语句把item传递给引擎
            yield item

其中的部分代码解释一下:
scrapy.Request是构造requests对象的类。url是我们往requests对象里传入的每家公司招聘信息网址的参数。

  • callback的中文意思是回调。self.parse_Jobhui_Info是我们新定义的parse_Jobhui_Info方法。往requests对象里传入callback=self.parse_GetJobInfo这个参数后,引擎就能知道response要前往的下一站,是用parse_Jobhui_Info()方法来解析传入的新参数。
  • yield的作用就是把这个构造好的requests对象传递给引擎。

(4)存储文件

Scrapy可以支持把数据存储成csv文件或者Excel文件:

  • 存储成csv文件的方法比较简单,只需在settings.py文件里,添加如下的代码即可:
FEED_URI = '%(Jobhui_Info)s.csv'
FEED_FORMAT = 'CSV'
FEED_EXPORT_ENCODING = 'ansi'
  • FEED_URI是导出文件的路径。’%(Jobhui_Info)s.csv’,是把CSV文件放到与settings.py文件同级的文件夹内。
  • FEED_FORMAT 是导出数据格式,写CSV就能得到CSV格式。
  • FEED_EXPORT_ENCODING 是导出文件编码,ansi是一种在windows上的编码格式,你也可以把它变成utf-8用在mac电脑上。

需要注意:在最新的scrapy版本2.1.0不支持这种方式。需要降级到1.8.0版本才能存储进csv文件中。

  • 存储成Excel文件,相对复杂一些。实现步骤如下:
  1. 先在setting.py里设置启用ITEM_PIPELINES。取消掉ITEM_PIPLINES的注释即可。
# Configure item pipelines

# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html

ITEM_PIPELINES = {

   'class14.pipelines.Class14Pipeline': 300,

}
  1. 在piplines.py文件中,编辑代码。依然是使用openpyxy来实现对数据的保存。
import openpyxl

# 定义一个 Class14Pipeline
class Class14Pipeline(object):
    # 初始化函数,当类实例化时该方法自动启动
    def __init__(self):
        # 创建工作簿
        self.wb = openpyxl.Workbook()
        # 定位工作表
        self.ws = self.wb.active
        # 使用append函数往表格中添加表头
        self.ws.append(['公司','职位','地址','招聘信息'])

    # process_item是默认的处理item的方法,就像parse是默认处理response的方法
    def process_item(self,item,spider):
        # 把公司名称、职位名称、工作地点和招聘要求都写成列表的形式,赋值给line
        line = [item['company'],item['position'],item['address'],item['info']]
        # 用append函数把公司名称、职位名称、工作地点和招聘要求的数据都添加进表格
        self.ws.append(line)
        # 将item给回引擎,如果后面还有这个item需要经过的itempipeline,引擎会自己调度
        return item

    # close_spider是当爬虫结束运行时,这个方法就会执行
    def close_spider(self, spider):
        # 保存文件
        self.wb.save('Jobhui_Info.xlsx')
        # 关闭文件
        self.wb.close()

(5)修改设置

在settings.py文件里添加请求头,以及把ROBOTSTXT_OBEY=True改成ROBOTSTXT_OBEY=False。

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36'}

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

同时,我们要将#DOWNLOAD_DELAY = 3这行取消注释,同时对DOWNLOAD_DELAY这个参数的值进行修改。

DOWNLOAD_DELAY的意思是下载延迟,这个参数的作用是控制爬虫的速度。3秒对于这个项目来说太慢了,我们要把下载延迟的时间改成0.5秒。

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 0.5

三、练习

  1. 使用scrapy爬取豆瓣图TOP250前2页的书籍的短评数据(包括书名、评论ID、短评内容),并存储成Excel文件。
    (1)创建项目
    在cmd窗口,cd到所在文件夹:“scrapy startproject Doubanbook_Comments"
    (2)定义Item
    在Items.py文件中,输入下列代码:
import scrapy

class DoubanbookCommentsItem(scrapy.Item):
    # 定义书籍名称的数据属性
    name = scrapy.Field()
    # 定义评论ID的数据属性
    commentID = scrapy.Field()
    # 定义短评内容点的数据属性
    comment = scrapy.Field()

(3)创建和编写爬虫文件
在spiders文件夹下,新建文件Doubanbook_Comments.py。并编写代码如下:

import scrapy
import bs4
from ..items import DoubanbookCommentsItem

# 定义一个爬虫类doubanbookComments
class Doubanbook_Comments(scrapy.Spider):
    # 定义爬虫名字
    name = 'Doubanbook_Comments'
    # 定义允许爬虫爬取的网址
    allowed_domains = ['book.douban.com']
    # 定义起始网址
    start_urls = []
    # 根据豆瓣图书的url规律,取前3页的Url,并添加进起始网址中(只取第一页的书籍)
    for i in range(1):
        url = 'https://book.douban.com/top250?start={}'.format(i*25)
        start_urls.append(url)

    # 定义parse方法,获取每本书籍详情页的链接
    def parse(self, response):
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        url_list = bs.find_all('div',class_="pl2")

        for url in url_list:
            # <a>标签的值就是书籍详情页的链接
            book_url = url.find('a')['href']
            # 为不给网站造成负担,仅抓取第一页的短评内容
            for i in range(1,2):
                comment_url = '{}comments/new?p={}'.format(book_url,i)
                # 用yield语句把构造好的request对象传递给引擎
                yield scrapy.Request(comment_url,callback=self.parse_Get_bookomments)

    # 定义新的处理方法parse_Get_bookcomments
    def parse_Get_bookcomments(self, response):
        bs = bs4.BeautifulSoup(response.text,'html.parser')
        # 提取到书名
        name = bs.find('h1').text.replace(' 短评','')
        # 提取到书籍评论列表
        comment_list = bs.find_all('div',class_='comment')
        # 遍历列表
        for comment in comment_list:
            # 实例化DoubanbookCommentsItem类
            item = DoubanbookCommentsItem()
            # 将书籍名称放回DoubanbookCommentsItem类中
            item['name'] = name
            # 将评论人名称放回DoubanbookCommentsItem类
            item['commentID'] = comment.find('span',class_='comment-info').find('a').text
            item['comment'] = comment.find('span',class_='short').text

            # 使用yield语句把item传递给引擎
            yield item

(4)存储文件

  1. 在setting.py文件中,将ITEM_PIPELINES的注释去掉。
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'Doubanbook_Comments.pipelines.DoubanbookCommentsPipeline': 300,
}
  1. 在pipelines.py文件中,使用openpyxy来实现对数据的存储。
import openpyxl


# 定义一个DoubanbookCommentsPipeline类,负责处理item
class DoubanbookCommentsPipeline(object):
    # 初始化函数,当类实例化时,自动启动该方法
    def __init__(self):
        # 创建工作簿
        self.wb = openpyxl.Workbook()
        # 创建工作表
        self.ws = self.wb.active
        # 添加表头
        self.ws.append(['书籍名称', '评论人', '评论内容'])

    # process_item是默认的处理item的方法,就像parse是默认处理response的方法
    def process_item(self, item, spider):
        # 将书籍名称、评论人、评论内容赋值
        line = [item['name'], item['commentID'], item['comment']]
        # 将对应的数据添加进表格中
        self.ws.append(line)

        return item

    # close_spider是当爬虫结束运行时,这个方法就会执行
    def close_spider(self, spider):
        # 保存文件
        self.wb.save('douban_bookcomments.xlsx')
        # 关闭文件
        self.wb.close()

(5)修改设置
在settings.py文件中,添加请求头,把ROBOTSTXT_OBEY=True改成ROBOTSTXT_OBEY=False。

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36'}


# Obey robots.txt rules
ROBOTSTXT_OBEY = False

同时修改一下下载延迟时间,调整到0.5秒

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 0.5
  1. 使用Scrapy,爬取当当网2018年图书销售榜单前3页的数据,包括排名、图书名、作者、出版社、书的原价、价格折扣、折扣价、评价数量。
    网址: http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-year-2019-0-1-1

实现步骤

1. 创建项目

在CMD中,cd到项目所在文件夹,创建项目,命名为DDbook。

scrapy startproject DDbook

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tV3ajZXJ-1592558823816)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3560fb1c-7733-4faa-9577-656c26190507/Untitled.png)]

2. 定义items

在items.py文件中,定义items。

import scrapy

class DdbookItem(scrapy.Item):
		# 定义书籍排名的数据属性
    num = scrapy.Field()
    # 定义书籍名称的数据属性
    name = scrapy.Field()
    # 定义作者的数据属性
    author = scrapy.Field()
    # 定义出版社的数据属性
    publish = scrapy.Field()
    # 定义书原价的数据属性
    price = scrapy.Field()
    # 定义书籍折扣的数据属性
    discount = scrapy.Field()
    # 定义折扣价的数据属性
    dis_price = scrapy.Field()
    # 定义评价数量的数据属性
    rating_num = scrapy.Field()

3. 创建和编写爬虫文件

在spiders文件夹下,创建DDbook.py文件,在其中编写爬虫代码:

import scrapy
import bs4
from ..items import DDbookItem

# 定义爬虫类DDbook,继承自scrapy.Spider
class DDbook(scrapy.Spider):
    # 定义爬虫的名称
    name = 'DDbook'
    # 定义允许爬虫爬取网址的域名
    allowed_domains = ['http://bang.dangdang.com']
    # 定义起始网页
    start_urls = []
    for i in range(1, 4):
        url = 'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-year-2019-0-1-{}'.format(i)
        start_urls.append(url)

    def parse(self, response):
        # 使用bs4解析数据
        bs = bs4.BeautifulSoup(response.text, 'html.parser')
        # 提取所有书籍的列表
        li_list = bs.find('ul', class_='bang_list clearfix bang_list_mode').find_all('li')
        # 遍历列表
        for book in li_list:
            # 实例化DDbook对象
            item = DDbookItem()
            # 将书籍排名放回DDbook的num属性
            item['num'] = book.find_all('div')[0].text.replace('.', '')
            # 将书籍名称放回DDbook类的name属性
            item['name'] = book.find('div', class_='name').find('a')['title']
            # 将作者放回DDbook类的author属性
						# 部分书籍没有作者,使用异常捕捉
						try:
                item['author'] = book.find_all('div', class_='publisher_info')[0].find('a').text
            
            except:
                item['author'] = None

            # 将出版社放回DDbook类的publish属性
            item['publish'] = book.find_all('div', class_='publisher_info')[1].find('a').text
            # 将书籍价格放回DDbook类的price属性
            item['price'] = book.find('span', class_='price_r').text
            # 将折扣放回DDbook的discount属性
            item['discount'] = book.find('span', class_='price_s').text
            # 将折扣价放回DDbook的dis_price属性
            item['dis_price'] = book.find('span', class_='price_n').text
            # 将评价数量放回到DDbook的rating_num属性
            item['rating_num'] = book.find('div', class_='star').find('a').text

            # yield语句把item传递给引擎
            yield item

4. 存储文件

采用CSV格式进行存储,在settings.py中输入如下代码:

FEED_URI = '%(name)s.csv'
FEED_FORMAT = 'CSV'
FEED_EXPORT_ENCODING = 'ansi'

需要注意:在最新的scrapy版本2.1.0不支持这种方式。需要降级到1.8.0版本才能存储进csv文件中。

5. 修改设置

在settings.py文件里添加请求头,以及把ROBOTSTXT_OBEY=True改成ROBOTSTXT_OBEY=False。

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36'}

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

同时修改一下下载延迟时间,调整到0.5秒。

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 0.5

6. 运行scrapy

在与scrapy.cfg文件同级中,建立main.py文件。输入以下代码,接着运行即可。

from scrapy import cmdline

cmdline.execute(['scrapy','crawl','DDbook'])
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章