Crawler168-01 | 爬取搞笑图片之青年图摘(站点已不能正常访问)

255 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

青年图摘

在个人的项目里面有一个对于图文素材进行获取的需求,于是需要在网络平台上获取图文素材。青年图摘就是其中一个,很不凑巧,该网站在2022年4月1日的时候停更了,在对网站进行分析的时候,发现图片使用的是新浪的图床,故在该网站服务停止运行的时候,有可能还能根据网址进行获取图片。所以本脚本是对图文相关数据进行存储。

分析

  • 由于网站文章停止了运营,所以需求将是对网站过去的文章内容进行获取,不存在增量爬虫的设定
  • 在网站资源url上,不遵循restful,也不支持显式分页,但是在每一篇文章上都拥有“上一页”超链接,故可以作为分页进行资源的获取
  • 网站没有进行javascript进行渲染,requests包可直接获取源码
  • 每一篇文章的主要内容位于 类选择器为post-contentdiv标签下
  • 在每一篇文章都拥有对图片的说明和图片的展示,分别在上述div之中的p列表里

代码

组织架构

Crawler
  |-- conf # 存放配置文件
    |-- settings.py # 配置文件
  |-- interial  # 内部组织,存放store相关
    |-- logger.py # 日志配置,使用loguru
    |-- mysql.py  # Mysq连接,使用pymysql
    |-- redis.py  # Redis连接,使用redis
  |-- logs  # 日志存放文件夹
  |-- spider
    |-- qntuzhai.py # 爬虫文件

爬虫实现

# filename: spider/qntuzhai.py
import json
​
import requests
from pyquery.pyquery import PyQuery as pq
from interial.redis import REDIS
from conf.settings import HEADERS
from interial.logger import logger, Secretary
​
​
def fetch(url, method='GET', **kwargs):
    try:
        rsp = requests.request(method, url, **kwargs)
        if rsp.status_code != 200:
            return False, rsp.text
        return rsp, "请求成功"
    except Exception as e:
        return False, e
​
​
@Secretary
def get_page(url):
    logger.info(f"Get Page: {url}")
    rsp, msg = fetch(url, headers=HEADERS)
    if not rsp:
        logger.error(msg)
        return
    doc = pq(rsp.text)
    ps = doc('div.post-content p').items()
    for p in ps:
        text = p.text().split("】")[-1]
        image_src = p("img").attr("src")
        item = {"title": text, "url": image_src}
        REDIS.lpush("crawler:recreator:item", json.dumps(item, ensure_ascii=False))
        logger.info(f"Push item: {item}")
​
    next_page = doc("div.prev-post a").attr("href")
    if next_page:
        next_page = f"https://www.qingniantuzhai.com{next_page}"
        return get_page(next_page)
    return
​
​
if __name__ == '__main__':
    url = "https://www.qingniantuzhai.com/qing-nian-tu-zhai-0331-4/"
    get_page(url)
​

运行结果:

...
2022-08-09 20:16:14.805 | INFO     | __main__:get_page:33 - Push item: {'title': '伤害不高,侮辱极强', 'url': 'https://wx4.sinaimg.cn/mw1024/0037UN1agy1gv59npxr2sg60990c04qw02.gif'}
2022-08-09 20:16:14.846 | INFO     | __main__:get_page:33 - Push item: {'title': '做的不正宗了,狗狗都开始感兴趣了', 'url': 'https://wx1.sinaimg.cn/mw1024/002iRMxrly1gv6tyft68mg607v08s7wi02.gif'}
...
# 共计 9343

其他文件

Redis

# filename: interial/redis.py
import redis
from conf.settings import REDIS_AUTH, REDIS_HOST, REDIS_PORT, REDIS_DB, USE_REDIS
​
REDIS_CONFIG = {
    'host': REDIS_HOST or "127.0.0.1",  # 数据库地址
    'port': REDIS_PORT or 6379,  # 端口号
    'db': REDIS_DB or 0,  # 数据库名称
    'password': REDIS_AUTH or "",  # 数据库密码
}
​
​
class RedisPool:
    _instance = None
​
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance
​
    _redis_pool_data = redis.ConnectionPool(decode_responses=True, **REDIS_CONFIG)
​
    # 实例化redis
    @classmethod
    def get_conn(cls):
        conn = redis.Redis(connection_pool=cls._redis_pool_data)
        return conn
​
​
REDIS = RedisPool.get_conn() if USE_REDIS else None

logger

import os
from datetime import datetime
from loguru import logger
from conf.settings import LOG_DIR
​
​
now = datetime.now()
file_name = os.path.join(LOG_DIR, f"crawler_{now.year}_{now.month}_{now.day}.log")
logger.add(file_name, format="{time} {level} {message}", level="INFO", rotation="10 MB", encoding="utf-8", enqueue=True)
​
​
class Secretary(object):
    """
        黑盒日志记录器:数据数据 > (执行) > 数据输出
    """
    def __init__(self, func):
        self.func = func
​
    def __call__(self, *args, **kwargs):
        res = self.func(*args, **kwargs)
        logger.info(f'{{"func": "{self.func.__name__}", "params": "{args, kwargs}", "res": "{res}"}}')

依赖

async-timeout==4.0.2
certifi==2022.6.15
charset-normalizer==2.1.0
cssselect==1.1.0
Deprecated==1.2.13
fake-useragent==0.1.11
idna==3.3
jsonpath==0.82
loguru==0.6.0
lxml==4.9.1
packaging==21.3
py==1.11.0
PyMySQL==1.0.2
pyparsing==3.0.9
pyquery==1.4.3
redis==4.3.4
requests==2.28.1
urllib3==1.26.11
wrapt==1.14.1