关于scrapy框架的使用及新闻推荐

新闻推荐系统整体设计

一、关于scrapy框架的使用—爬取新浪新闻数据

首先,新建一个scrapy项目

scrapy startproject <项目名>

项目结构如下所示:

在这里插入图片描述

上图中有部分几个是之后新建的,若新建之后没有这些内容,也不用担心。

接下来,让我们看一下scrapy框架的数据流动过程

img

Scrapy数据流是由执行的核心引擎(engine)控制,流程是这样的:

1、爬虫引擎获得初始请求开始抓取。
2、爬虫引擎开始请求调度程序,并准备对下一次的请求进行抓取。
3、爬虫调度器返回下一个请求给爬虫引擎。
4、引擎请求发送到下载器,通过下载中间件下载网络数据。
5、一旦下载器完成页面下载,将下载结果返回给爬虫引擎。
6、引擎将下载器的响应通过中间件返回给爬虫进行处理。
7、爬虫处理响应,并通过中间件返回处理后的items,以及新的请求给引擎。
8、引擎发送处理后的items到项目管道,然后把处理结果返回给调度器,调度器计划处理下一个请求抓取。
9、重复该过程(继续步骤1),直到爬取完所有的url请求。
爬虫引擎(ENGINE)
爬虫引擎负责控制各个组件之间的数据流,当某些操作触发事件后都是通过engine来处理。

调度器
调度接收来engine的请求并将请求放入队列中,并通过事件返回给engine。

下载器
通过engine请求下载网络数据并将结果响应给engine。

Spider
Spider发出请求,并处理engine返回给它下载器响应数据,以items和规则内的数据请求(urls)返回给engine。

管道项目(item pipeline)
负责处理engine返回spider解析后的数据,并且将数据持久化,例如将数据存入数据库或者文件。

下载中间件
下载中间件是engine和下载器交互组件,以钩子(插件)的形式存在,可以代替接收请求、处理数据的下载以及将结果响应给engine。

spider中间件
spider中间件是engine和spider之间的交互组件,以钩子(插件)的形式存在,可以代替处理response以及返回给engine items及新的请求集。
这个是参考这个博主的原文链接放在这里:https://blog.csdn.net/Yuyh131/article/details/83651875

实际案例
一、item.py文件处理

在我们创建完项目之后,首先我们可以先进入items.py,这个文件是对你要爬取到的数据的一个类似于实体类一样的存在。
在这里插入图片描述

import scrapy


class NewsspirderItem(scrapy.Item):
    table_name = scrapy.Field()
    title = scrapy.Field()
    url = scrapy.Field()
    date = scrapy.Field()
    mainpage = scrapy.Field()
    comments = scrapy.Field()
    origin_url = scrapy.Field()
    keywords = scrapy.Field()
    contentHtml = scrapy.Field()
    author = scrapy.Field()
    # video_url = scrapy.Field()
    # pic_url = scrapy.Field()
    type = scrapy.Field()
二、新建spider进行处理

之后到spiders目录下面,新建爬虫文件newsSpider_fun.py

部分代码如下所示:

from copy import deepcopy

import scrapy

from newsSpirder import settings
from newsSpirder.items import NewsspirderItem
from newsSpirder.items import NewsUrl
import json
import re
import time

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36'
}


### 此模块爬取体育和娱乐新闻

class newsSpider_fun(scrapy.Spider):
    name = 'newsSpider_fun'
    allo_domain = 'ent.sina.com.cn'
    base_url = ['https://ent.sina.com.cn/',  # 娱乐1
                'http://sports.sina.com.cn/',  # 体育2
                'https://cul.news.sina.com.cn/'  # 文化3

                ]
    start_urls = []

    def start_requests(self):
        for i in range(0, settings.PAGE):
            urls = [
             'https://interface.sina.cn/pc_api/public_news_data.d.json?callback=jQuery111203513011348270476_1675935832328&cids=209211&pdps=PDPS000000060130%2CPDPS000000066866&smartFlow=&type=std_news%2Cstd_slide%2Cstd_video&pageSize=20&top_id=hencxtu1691422%2Chencxtu5974075%2Chencxtu5919005%2Chencxtu5908111&mod=nt_culture0&cTime=1483200000&up=' + str(
                    i) + '&action=1&tm=1675935836&_=1675935832332'  # 文化
            ]  # 娱乐
            for url in urls:
                yield scrapy.Request(url=url, callback=self.parse, headers=headers, dont_filter=False)

    def parse(self, response):
        data = response.body.decode('utf8')

        data = re.findall(r'[(](.*)[)]', str(data).replace('\n', ''))
        contents = list(filter(None, json.loads(data[0])['data']))
        for content in contents:
            item = NewsspirderItem()
            item['table_name'] = 'news_newdetail'
            item["url"] = content['url']  # 链接
            item["date"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(content['ctime']))  # 日期
            item["title"] = content['title']  # 标题
            try:
                item["origin_url"] = content['orgUrl']  # 原链接
                item["keywords"] = content['labels']  # 关键字
                if str(content['category']).find('娱乐') != -1:
                    item["type"] = '1'  # 类型
                elif str(content['category']).find('体育') != -1:
                    item["type"] = '2'
                else:
                    item['type'] = '-1'
            except:
                try:
                    item["keywords"] = content['tags']
                except:
                    item["keywords"] = ""
                item['type'] = '3'
                item["origin_url"] = item['url']

            item["author"] = content['author']  # 作者
            yield scrapy.Request(item["url"], callback=self.parse_item2, meta={"item": deepcopy(item)}, headers=headers, dont_filter=False)

    def parse_item2(self, response):
        url_item = NewsUrl()
        url_item['table_name'] = 'news_urlcollect'
        news_item = response.meta['item']
        news_item["mainpage"] = ''.join(response.xpath('//*[@id="article"]//text()').extract())
        content_html = ''.join(response.xpath('//*[@id="article"]').extract())
        news_item["contentHtml"] = content_html
        if str(content_html) != '':
            yield news_item
            url_item["url"] = news_item["url"]
            url_item["handle"] = 0
            url_item["type"] = news_item['type']
            url_item["time"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            yield url_item

对这个文件进行具体的讲解

在这里插入图片描述

这个name就是我们启动爬虫时候的名字。allow_domain是允许的域名。

在start_requests下面定义的是要爬取网站的链接。

yield scrapy.Request会发出请求,并回调解析函数(parse)。

此时就可以在parse函数里面对爬取到的页面进行数据处理。

当需要进入到三级页面时候,我们也可以在parse函数后面在发起request请求,并回调parse_item2

当我们需要在parse_item2中需要parse函数中的item时,仅需要将参数这样放入request请求中即可。

在这里插入图片描述

然后在第二个函数中将数据给取出

在这里插入图片描述

注意:scrapy默认的是有链接去重的

dont_Filter = True

这是将去重给关闭的!!!

三、pipeline.py文件处理

部分代码如下所示

import copy

from itemadapter import ItemAdapter
from scrapy.exceptions import DropItem

from newsSpirder.items import NewsspirderItem, NewsUrl
from newsSpirder.tool import process_str, process_url
from newsSpirder.tool import process_html
import MySQLdb
from .settings import mysql_host, mysql_db, mysql_user, mysql_passwd, mysql_port


class NewsspirderPipeline:  # 对爬取到的信息进行处理
    def process_item(self, item, spider):
        item['url'] = process_url(item['url'])
        if type(item) == NewsspirderItem:
            if item['author'] == '':
                item['author'] = '未知'
            item['mainpage'] = process_str(item['mainpage'])
            item['contentHtml'] = process_html(item['contentHtml'])
            item['comments'] = '测试'
            item['keywords'] = str(item['keywords'])
        return copy.deepcopy(item)


class CheckPipeline:  # 去重
    """check item, and drop the duplicate one"""

    def __init__(self):
        self.names_seen = set()
        self.url_seen = set()

    def process_item(self, item, spider):
        if type(item) == NewsspirderItem:
            if item['title']:
                if item['title'] in self.names_seen:
                    DropItem("Duplicate item found: %s" % item)
                else:
                    self.names_seen.add(item['title'])
                    return item
            else:
                DropItem("Missing price in %s" % item)
        elif type(item) == NewsUrl:
            if item['url']:
                if item['url'] in self.url_seen:
                    DropItem("Duplicate item found: %s" % item)
                else:
                    self.url_seen.add(item['url'])
                    return item
            else:
                DropItem("Missing price in %s" % item)

对pipeline文件进行处理需要在settings文件中设置优先级

ITEM_PIPELINES = {
   'newsSpirder.pipelines.NewsspirderPipeline': 50,
   'newsSpirder.pipelines.CheckPipeline': 75,
   'newsSpirder.pipelines.NewssqlPipeline': 100
}

优先级的高低是 数字小的 item先进入其中进行处理。

上述的优先级是先进入 NewsspiderPipeline 、然后到CheckPipeline,以此类推。

保存到数据库的函数是第三个类。这里面就不展开论述。

由于我所爬取的使用了两个item,因此在第一个spider文件中需要yield两次不同的item,在pipeline中进行处理时会传入两个item。而这个保存至mysql数据库会自动保存不同的item到不同的数据库表。

运行单个爬虫的命令为:

scrapy crawl 爬虫名

若要多个爬虫一块运行,则可以新建一个main.py文件运行,代码如下:

在这里插入图片描述

这里还有一个小坑

由于scrapy的异步特质,保存至数据库时会出现重复数据,因此可以通过在数据库中进行设置以及在pipeline中进行处理重复的item的方式来处理。去重的代码也在上文。

二、推荐算法的选择

1.基于物品的协同过滤算法(ItemCF)原理

ItemCF算法是目前业界使用最广泛的算法之一,亚马逊、Netflix、YouTube的推荐算法的基础都是基于ItemCF。
不知道大家平时在网上购物的时候有没有这样的体验,比如你在网上商城下单了一个手机,在订单完成的界面,网页会给你推荐同款手机的手机壳,你此时很可能就会点进去浏览一下,顺便买一个手机壳。其实这就是ItemCF算法在背后默默工作。ItemCF算法给用户推荐那些和他们之前喜欢的物品相似的物品。因为你之前买了手机,ItemCF算法计算出来手机壳与手机之间的相似度较大,所以给你推荐了一个手机壳,这就是它的工作原理。看起来是不是跟UserCF算法很相似是不是?只不过这次不再是计算用户之间的相似度,而是换成了计算物品之间的相似度。

由上述描述可以知道ItemCF算法的主要步骤如下:

  1. 计算物品之间的相似度
  2. 根据物品的相似度和用户的历史行为给用户生成推荐列表

那么摆在我们面前的第一个问题就是如何计算物品之间的相似度,这里尤其要特别注意一下:

ItemCF算法并不是直接根据物品本身的属性来计算相似度,而是通过分析用户的行为来计算物品之间的相似度。

什么意思呢?比如手机和手机壳,除了形状相似之外没有什么其它的相似点,直接计算相似度似乎也无从下手。但是换个角度来考虑这个问题,如果有很多个用户在买了手机的同时,又买了手机壳,那是不是可以认为手机和手机壳比较相似呢?
由此引出物品相似度的计算公式:
此部分内容可以参考下面给出的链接,由于图片过多,上传不太方便,就放的链接。
参考链接:

基于物品的协同过滤算法(ItemCF)原理以及代码实践 - 简书 (jianshu.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值