1. 程式人生 > >爬蟲框架開發(4)--- 專案實戰——>新浪滾動新聞資訊實時資料採集

爬蟲框架開發(4)--- 專案實戰——>新浪滾動新聞資訊實時資料採集

要求:

  1. 儲存文章的標題、作者、釋出時間、正文、正文中的圖片連結、文章連結、文章所屬分類
  2. 根據網站的實時更新(週期1分鐘)進行採集
  3. 時間格式儲存為"yyyy-mm-dd HH:MM:SS"
  4. 儲存到mysql資料庫

程式碼實現如下:

新浪滾動的爬蟲檔案:

# spiders/sina_gundong.py
import time

from scrapy_plus.core.spider import Spider
from scrapy_plus.http.request import Request
from scrapy_plus.item import Item
import js2py


class SinaGunDong(Spider):

    name = "sina_gundong"

    headers = {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "Cookie": "UOR=www.google.com,www.sina.com.cn,; SGUID=1520816292777_83076650; SINAGLOBAL=211.103.136.242_1520816292.736990; SUB=_2AkMt-V_2f8NxqwJRmPEQy2vmZYx_zwjEieKbpa4tJRMyHRl-yD83qnIJtRB6BnlxGSLw2fy6O04cZUKTsCZUeiiFEsZE; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WhpFUZmqbYYLueonGrZIL2c; U_TRS1=0000001a.e268c0.5aaa0d39.35b0731a; lxlrttp=1521688012; Apache=223.72.62.219_1522208561.132697; ULV=1522208952476:6:6:3:223.72.62.219_1522208561.132697:1522208561158; U_TRS2=000000db.81c2323e.5abca69b.ad269c11; ArtiFSize=14; rotatecount=1; hqEtagMode=1",
        # "Host": "roll.news.sina.com.cn",   這裡host必須禁用掉
        "Pragma": "no-cache",
        "Referer": "http://roll.news.sina.com.cn/s/channel.php?ch=01",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
    }

    def start_requests(self):
        while True:
            # 需要發起這個請求,才能獲取到列表頁資料,並且返回的是一個js語句
            url = "http://roll.news.sina.com.cn/interface/rollnews_ch_out_interface.php?col=89&spec=&type=&ch=&k=&offset_page=0&offset_num=0&num=120&asc=&page=1&r=0.5559616678192825"
            yield Request(url, parse='parse', filter=False)
            time.sleep(60)     # 每60秒發起一次請求

    def parse(self, response):
        '''響應體資料是js程式碼'''
        # 使用js2py模組,執行js程式碼,獲取資料
        ret = js2py.eval_js(response.body.decode("gbk"))    # 對網站分析發現,資料編碼格式是gbk的,因此需要先進行解碼
        for news in ret.list:    #
            yield Request(news["url"], headers=self.headers, parse='parse_detail', meta={"type": news["channel"]["title"]})

    def parse_detail(self, response):
        response.body = response.body.decode("utf-8")    # 部分頁面無法正確解碼,因此在這裡手動進行解碼操作
        title = response.xpath("//h1[@class='main-title']/text()")[0]
        pub_date = response.xpath("//span[@class='date']/text()")[0]
        try:
            author = response.xpath("//div[@class='date-source']//a/text()")[0]    # 由於作者的提取,有兩種格式,因此這裡使用一個異常捕獲來進行判斷
        except IndexError:
            author = response.xpath("//div[@class='date-source']//span[contains(@class,'source')]/text()")[0]
        content = response.xpath("//div[@class='article']//text()")    # 多個  每一個代表一段
        image_links = response.xpath("//div[@class='article']//img/@src")    # 圖片連結有多個

        yield Item({
            "content": content,    # 正文
            "image_links":image_links,    # 圖片連結
            "title": title,    # 標題
            "pub_date":pub_date,    # 釋出日期
            "author": author,    # 作者
            "url": response.url,    # 文章連結
            "type": response.request.meta["type"],    # 文章所屬分類
        }
    )

專案中新建db.py

# 專案資料夾下db.py
# 依賴:sqlalchemy  pymysql
from sqlalchemy import Column,Integer,Text,DateTime, String
from sqlalchemy.ext.declarative import declarative_base

# 建立物件的基類:
Base = declarative_base()


class Model(Base):
    __tablename__ = 'sina_news'

    id = Column(Integer, primary_key=True, autoincrement=True)    # 主鍵id
    title = Column(String(100), nullable=False)    # 標題
    author = Column(String(20), nullable=False)    # 作者
    pub_date = Column(DateTime, nullable=False)    # 釋出時間
    content = Column(Text, nullable=False)    # 正文
    image_links = Column(Text, nullable=False)    # 圖片連結
    url = Column(String(500), nullable=False)    # 文章連結
    type = Column(String(6), nullable=False)    # 文章分類
    news_tag = Column(String(40), nullable=False)    # 文章去重標記

管道檔案:

# 專案下管道檔案 pipelines.py

import json
from datetime import datetime
from hashlib import sha1

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from scrapy_plus.core.scheduler import utf8_string

from db import Base, Model


class Pipeline(object):
    '''資料入庫前的清洗和格式化處理'''

    def process_item(self, item, spider):
        item.data['pub_date'] = datetime.strptime(item.data['pub_date'], '%Y年%m月%d日 %H:%M') # 將時間格式進行一個處理,轉換為datetime型別
        item.data['content'] = [i for i in item.data['content'] if i.strip()]    # 去掉content中的空白字元
        item.data['content'] = "\n\n".join(item.data["content"])
        item.data['image_links'] = json.dumps(item.data['image_links'])  # 列表轉換為json字串
        # 資料去重標識生成:利用標題、作者、文章連結生成唯一key
        s1 = sha1()
        s1.update(utf8_string(item.data['title']))
        s1.update(utf8_string(item.data['author']))
        s1.update(utf8_string(item.data['url']))
        item.data['news_tag'] = s1.hexdigest()    # 資料去重標識
        return item


class MysqlPipeline(object):

    def __init__(self):
        # 建立資料庫連結
        self.conn = create_engine("mysql+pymysql://root:
[email protected]
/test3?charset=utf8") Base.metadata.create_all(bind=self.conn) # 建立表,如果有,就不在建立 def _get_session(self): # 建立session物件 Session = sessionmaker(bind=self.conn) return Session() def process_item(self, item, spider): session = self._get_session() # 獲取session # 先判斷news_tag是否已經存在:如果存在,代表資料是重複的,否則才插入 if not session.query(Model).filter_by(news_tag=item.data['news_tag']).all(): obj = Model(**item.data) # 建立模型類物件 session.add(obj) # 插入資料 session.commit() # 提交 session.close() # 關閉session return item

專案配置檔案:

# 更改預設的配置
DEFAULT_LOG_FILENAME = '滾動新聞採集.log'    # 預設日誌檔名稱


SPIDERS = [
    "spiders.sina.SinaGunDong"
]

PIPELINES = [
    "pipelines.Pipeline",
    "pipelines.MysqlPipeline"
]

SPIDER_MIDS = [
]

DOWNLOADER_MIDS = [
]

# 控制最大併發數
MAX_ASYNC_NUMBER = 1

# 非同步模式  thread, coroutine
ASYNC_TYPE = 'thread'

'''分散式配置'''

# 執行角色
# None 代表非分散式,發起初始請求(_start_requests), 處理請求(_execute_request_response_item)
# master代表主,只負責發起初始請求(_start_requests),並維護請求佇列
# slave代表從,只負責處理請求(_execute_request_response_item)
# ROLE = 'master'
# ROLE = 'slave'
ROLE = None

# 最大重試次數
MAX_RETRY_TIMES = 3

# redis 佇列的配置
REDIS_QUEUE_NAME = 'request_queue'
REDIS_QUEUE_HOST = 'localhost'
REDIS_QUEUE_PORT = 6379
REDIS_QUEUE_DB = 10

# reids 集合配置
REDIS_SET_NAME = 'filter_container'
REDIS_SET_HOST = 'localhost'
REDIS_SET_PORT = 6379
REDIS_SET_DB = 10

# 利用redis進行請求備份 的配置
REDIS_BACKUP_NAME = 'request_backup'
REDIS_BACKUP_HOST = 'localhost'
REDIS_BACKUP_PORT = 6379
REDIS_BACKUP_DB = 10