Scrapy框架----数据建模与请求

一: 建立数据模型:

  • 1:定义数据模型的作用:提前规划好要爬取那些数据。
  • 2:定义数据模型的位置:items.py

1:在items.py文件中自定义要提取的字段:

import scrapy
class MyspiderItem(scrapy.Item):
    name = scrapy.Field()
    level = scrapy.Field()
    text = scrapy.Field()

2:在itcast.py中只能使用这三个定义的,别的就会报错。

class ItcastSpider(scrapy.Spider):
   ...
    def parse(self, response):
        """解析url地址返回的响应对象"""
        # 响应对象,可以直接执行xpath语法,提取二进制文本数据: response.body
        li_list = response.xpath("//div[@class='tea_con']//ul//li")
        for li in li_list:
            item = {}
            item['name'] = li.xpath(".//h3/text()").extract_first()
            item['level'] = li.xpath(".//h4/text()").extract_first()
            item['text'] = li.xpath(".//p/text()").extract_first()

            # 将数据交付给引擎,再由引擎传递到数据管道中。
            yield item

二:Scrapy如何实现翻页请求:

需求:爬取千千音乐的43页歌单列表的标题:

1:前端网页分析

1: url地址分析:

https://music.taihe.com/songlist
https://music.taihe.com/songlist?subCateId=&pageNo=2
https://music.taihe.com/songlist?subCateId=&pageNo=43
说明:地址是由于pageNo来决定的。

2: 分析,首先我要获取最顶层的div标签的Xpath语法:

//[@id="__layout"]/div/div[2]/div[3]/div
//
[@id="__layout"]/div/div[2]/div[3]/div[16]/div[2]

在这里插入图片描述

2:编写代码:

1:创建爬虫:
在这里插入图片描述
2:代码编写:
items.py中创建模板:

class QianQianItem(scrapy.Item):
    title = scrapy.Field()

qianqian_music.py:

import scrapy
from myspider.items import QianQianItem


def all_url():
    for i in range(1, 44):
        url = 'https://music.taihe.com/songlist' + "?subCateId=&pageNo={}".format(i + 1)
        yield url, i + 1

class QianqianMusicSpider(scrapy.Spider):
    # 爬虫的名字
    name = 'qianqian_music'
    # 爬虫允许爬取的域范围
    allowed_domains = ['https://music.taihe.com/']
    # 起始的url
    start_urls = ['https://music.taihe.com/songlist']
    # 创建生成url的生成器
    make_url = all_url()


    def parse(self, response):

        # 1: 先分组,再分组提取数据:
        tr_list = response.xpath('//div[@class = "tracklist-item fl"]')
        # 2: 提取每一页的数据:
        for tr in tr_list:
            item = QianQianItem()
            item['title'] = tr.xpath("./div[2]/text()").extract_first()
            print(item)

        # 3: 构建下一页url请求:
        # https: // music.taihe.com / songlist?subCateId = & pageNo = 2
        url, i = next(self.make_url)
        print(url)
        if i != 45:
            print("第{}页。。。。。。。。。。。。".format(i))
            next_request = scrapy.Request(url=url, callback=self.parse, dont_filter=True)
            yield next_request

3: 代码出现的问题:
错误一:

Scrapy框架底层会将重复的url请求进行去重,我们的url由于不是完全一致的,但是Scrapy发现我们url都是差不多的,所以除了第一个全部给去掉了。
解决方案:在scrapy.Request(url=url, callback=self.parse, dont_filter=True)中加入:dont_filter=True。
或者:allowed_domains = [‘https://music.taihe.com/’]中加入这个url。

错误二:创建生成器的要让他变成一个类属性,这样每次next()函数会调用一次。如果直接 url, i = next(all_url()),导致每次执行都是第一次执行yield关键字停止的状态。执行结果就是死循环爬取网站第二页。

在这里插入图片描述

4: 运行测试:
在这里插入图片描述

三:scrapy.Request参数:

  • 1: url :请求的url。
  • 2:callback : 当前的url应该交给哪个响应处理。
  • 3:meta:不同解析函数之间传递。
  • 4:dont_filter: 默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动。
  • 5:method: 默认是get,如果是post则需要指定。
  • 6:headers : 接收一个字典,其中不包括cookies
  • 7:cookies: 接收一个字典,专门放置cookies
  • 8:body: 接收json字符串,为POST的数据,发送payload_post请求时使用。

四:案例—爬取网易招聘信息:

1: 准备工作:

目的:通过爬取网易招聘的页面的招聘信息,学习如何实现翻页请求。

1: 更改配置文件:

ROBOTSTXT_OBEY = False # 表示忽略robots.txt协议,
USER_AGENT = ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36’ # 设置user-Agent ,每次请求都会携带。

2:创建爬虫:
在这里插入图片描述
3:前端分析:
分析前端代码可知,首先要获取tr标签,然后再获取里面的a标签的内容。

在这里插入图片描述
分析下一页操作,发现每次生成一页,在网页中的这个标签都会生成下一页的url。
在这里插入图片描述

2: 代码实现:

1:模型类:

class JobItem(scrapy.Item):
    name = scrapy.Field()

2: 爬虫代码:

import scrapy
from myspider.items import JobItem
class JsbSpiderSpider(scrapy.Spider):
    name = 'jsb_spider'
    allowed_domains = ['https://hr.163.com/']
    start_urls = ['https://hr.163.com/position/list.do']

    def parse(self, response):
        # 提取响应数据,获取数据中的tr列表
        tr_list = response.xpath("//table[@class='position-tb']//tr")
        # 去除数据为空的tr标签: 因为我们发现所有的偶数的tr标签都是空的。
        tr_list = [tr for tr in tr_list if tr_list.index(tr) % 2 != 0]
        # 遍历取出每一条数据:
        for tr in tr_list:
            item = JobItem()
            item['name'] = tr.xpath("./td[1]/a/text()").extract_first()
            print(item)

        # 获取下一页的url地址:
        next_url = response.xpath("//a[text()='>']/@href").extract_first()
        if next_url != "javascript:void(0)":
            full_url = self.start_urls[0] + next_url
            yield scrapy.Request(url=full_url, callback=self.parse, dont_filter=True)

3: 爬虫测试:
爬到173页,网站崩了,不好意思。
在这里插入图片描述

五:meta参数的使用:

  • 1:meta可以实现在不同的解析函数中的进行参数传递。

分析:

首先,引擎会将首页的响应传递给parse函数,parse函数中,可以先获得职位的列表标签,然后遍历列表标签,拿到列表中的职位名称。然后在标签中获取详情页面的请求,构建请求,使用yield,将请求传给引擎。引擎会将详情页的响应传递给parse_detail,还会把parse函数传递给他的meta字典传递给他。那么在parse_detail中可以根据响应拿到职位的详情信息,可以根据response.meta获取职位名称。

案例:首页获取职位名称,详情页获取发布时间:

import scrapy
from ..items import JobDetaiItem

class JobDetailSpider(scrapy.Spider):
    """
    需求:爬取首页的职位名称,然后将item对象传给详情页,爬取详情页的发布时间
    """
    name = 'job_detail'
    allowed_domains = ['163.com']
    start_urls = ['https://hr.163.com/position/list.do']

    def parse(self, response):
        # 1.从响应对象中提取tr标签列表
        tr_list = response.xpath("//table[@class='position-tb']//tr")
        # 2.过滤掉空的tr标签
        tr_list = [tr for tr in tr_list if tr_list.index(tr) % 2 != 0]

        # 3.遍历提取职位名称
        for tr in tr_list:
            # 创建模型对象
            item = JobDetaiItem()
            # extract_first() 提取列表中第一条数据
            item["name"] = tr.xpath("./td[1]/a/text()").extract_first()

            # 往详情页发起请求,抓取数据
            detail_url = tr.xpath("./td[1]/a/@href").extract_first()
            # 1.构建详情页完整url地址
            full_path = "https://hr.163.com/" + detail_url
            # 2.构建请求,
            request = scrapy.Request(url=full_path,
                                     callback=self.parse_detail,
                                     meta={"item": item})
            # 3.并且yield给引擎
            yield request

    def parse_detail(self, response):
        """
        解析详情页面的响应
        :param response: 详情页响应对象
        :return:
        """
        # 1.从响应中获取item模型对象
        item = response.meta["item"]
        # 2.提取发布时间数据添加到模型对象中
        item["date"] = response.xpath("//p[@class='post-date']/text()").extract_first()
        print(item)

六:使用scrapy发送post请求:

  • 1: 需要重写父类的start_requests方法。
  • 2:scrapy.FormRequest(url=url, formdata=post_body, callback=self.parse)
  • 3:注意尽量不要使用:scrapy.Request发送post请求。

案例:金山翻译:
1: 创建爬虫:
在这里插入图片描述
2:代码展示:

import scrapy
import json

class JinshanSpiderSpider(scrapy.Spider):
    name = 'jinshan_spider'
    allowed_domains = ['http://fy.iciba.com/']
    start_urls = ['http://fy.iciba.com/ajax.php?a=fy']

    # 如果要发送post请求,需要重写父类的start_requests方法
    def start_requests(self):
        # 1:构建请求体字典
        post_body = {
            "f": "auto",
            "t": "auto",
            "w": "您好",
        }
        # 2:构建post请求对象
        for url in self.start_urls:
            post_request = scrapy.FormRequest(url=url, formdata=post_body, callback=self.parse)
            # 交付给引擎
            yield post_request

    def parse(self, response):
        json_str = response.body.decode()
        data_dict = json.loads(json_str)
        try:
            ret = data_dict["content"]["word_mean"]
        except:
            ret = data_dict["content"]["out"]
        print(ret)

七:Scrapy框架携带cookie发送post请求:

  • 只需要发送请求的时候携带上Cookie字典:scrapy.Request(url=url, cookies=cookie_dict, callback=self.parse)

案例:模拟github登录:

思路:发送请求的时候携带cookie

import scrapy


class GithubSpider(scrapy.Spider):
    name = 'github'
    allowed_domains = ['github.com']
    start_urls = ['https://github.com/TmacChenQian']

    # 重写start_urls起始URL地址构建请求的方法
    def start_requests(self):
          # 1.准备cookie字符串
        cookie_str = "复制cookie字符串"
                # 2.cookie字符串转换成字典
        cookie_dict = {cookie.split("=")[0]: cookie.split("=")[1] for cookie in cookie_str.split("; ")}
                # 3.自己构建一个GET请求,携带cookie参数
        for url in self.start_urls:
                 # 4.yield将请求交给引擎
            yield scrapy.Request(url=url, cookies=cookie_dict, callback=self.parse)

    # 解析响应
    def parse(self, response):
        with open("github.html", "w", encoding='utf-8') as f:
            f.write(response.body.decode())

八:使用scrapy.Formrequest.from_response登陆github

  • scrapy.Formrequest.from_response,能够自动提交表单。

思路:先向登录页面发送请求,获取响应。
然后在响应中定位form表单(formxpath="//div[@id=‘login’]/form",),注意,必须是有登录密码的那个表单。
然后使用 formdata={“login”: “账号”, “password”: “密码”},向表单中增加账号和密码,这里login和password指的是标签的name属性的值。
最后自动提交表单,完成登录请求。

import scrapy
class Github1Spider(scrapy.Spider):
    name = 'github1'
    allowed_domains = ['github.com']
    start_urls = ['https://github.com/login']

    def parse(self, response):
          # response 响应对象
        # formxpath 使用xpath语法提取form表单
        # formdata 向表单中填充数据
        # callback 回调解析函数
        yield scrapy.FormRequest.from_response(response,
                                        formxpath="//div[@id='login']/form",
                                        formdata={"login": "账号", "password": "密码"},
                                        callback=self.login_parse)

    def login_parse(self, response):
        with open("github6.html", "w", encoding='utf-8') as f:
            f.write(response.body.decode())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奈何碎银没有几两

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值