Scrapy 爬取北京公交相关信息

2024-09-17 13:46:29 浏览数 (2)

前提准备

  • 数据库建表
代码语言:sql复制
-- 使用数据库并建表
use studb;
CREATE TABLE `stu_businfo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `bus_name` text,
  `bus_type` text,
  `bus_time` text,
  `ticket` text,
  `gongsi` text,
  `gengxin` text,
  `licheng` text,
  `wang_info` text,
  `wang_buff` text,
  `fan_info` text,
  `fan_buff` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=142 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  • dos-cmd创建scrapy项目
代码语言:shell复制
# 创建一个 Scrapy 项目
scrapy startproject beibus

# 在项目中生成一个爬虫,指定域名
scrapy genspider bei_bus beijing.8684.cn

框架理解

spiders:放置spider代码的目录,用于编写用户自定义的爬虫

items.py:项目中的item文件,用于定义用户要抓取的字段

pipelines.py:管道文件,当spider抓取到数据以后,这些信息在这里会被重新分配

settings.py:项目的设置文件,用来设置爬虫的默认信息,及相关功能的开启与否

middlewares.py:主要是对功能的拓展,用于用户添加一些自定义的功能

  • 我们主要针对settings 、pipelines、 items、 spiders.bei_bus 进行对应程序的设置

程序

settings.py

代码语言:python代码运行次数:0复制
BOT_NAME = "beibus"

# TODO 默认属性
SPIDER_MODULES = ["beibus.spiders"]
NEWSPIDER_MODULE = "beibus.spiders"

# TODO 使爬虫不遵循Robots协议
ROBOTSTXT_OBEY = False

# TODO 解除注释,添加User-Agent属性
DEFAULT_REQUEST_HEADERS = {
   "Accept": "text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8",
   "Accept-Language": "en",
   'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
}

# TODO 解除注释,并将其中的内容改为如下:
ITEM_PIPELINES = {"beibus.Pipelines.MySQLPipelines": 300,} # 数字代表优先级,数字越小,优先级越高

# TODO 默认属性
REQUEST_FINGERPRINTER_IMPLEMENTATION = "2.7"
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"

# TODO 数据库相关参数
DB_HOST = "192.168.10.30"
DB_USER = "root"
DB_PWD = "000000"
DB = "studb"
DB_CHARSET = 'utf8'

Items.py

代码语言:python代码运行次数:0复制
import scrapy

class BeibusItem(scrapy.Item):
    """
    TODO BeibusItem 类用于定义爬取的公交信息数据结构。
        该类继承自 scrapy.Item,允许我们定义要提取的字段。
        每个字段对应于公交信息的不同属性。
    """

    # TODO 公交名称
    bus_name = scrapy.Field()

    # TODO 公交类型
    bus_type = scrapy.Field()

    # TODO 公交的运营时间
    bus_time = scrapy.Field()

    # TODO 公交票价
    ticket = scrapy.Field()
    
    # TODO 公交公司名称
    gongsi = scrapy.Field()

    # TODO 公交信息的最后更新时间
    gengxin = scrapy.Field()

    # TODO 公交的行驶里程
    licheng = scrapy.Field()

    # TODO 往程信息
    wang_info = scrapy.Field()
    
    # TODO 往程的具体路线
    wang_buff = scrapy.Field()

    # TODO 返程信息
    fan_info = scrapy.Field()

    # TODO 返程的具体路线
    fan_buff = scrapy.Field()

Pipelines.py

代码语言:python代码运行次数:0复制
import pymysql
from . import settings

class MySQLPipelines(object):
    # TODO 初始化参数
    def __init__(self):
        self.host = settings.DB_HOST
        self.user = settings.DB_USER
        self.pwd = settings.DB_PWD
        self.db = settings.DB
        self.charset = settings.DB_CHARSET
        self.connect()

    # TODO 数据库连接
    def connect(self):
        self.conn = pymysql.connect(host=self.host,
                                    user=self.user,
                                    password=self.pwd,
                                    db=self.db,
                                    charset=self.charset)

        self.cursor = self.conn.cursor()

    # TODO 向数据库中插入数据
    def process_item(self, item , spider):
        sql = ('INSERT INTO stu_businfo '
               '(bus_name, bus_type, bus_time, ticket, gongsi, gengxin, licheng, wang_info, wang_buff, fan_info, fan_buff) '
               'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)')

        self.cursor.execute(sql, (
            item['bus_name'], item['bus_type'], item['bus_time'], item['ticket'],
            item['gongsi'], item['gengxin'], item['licheng'], item['wang_info'],
            item['wang_buff'], item['fan_info'], item['fan_buff']
        ))

        self.conn.commit()
        return item

    # TODO 关闭数据库连接
    def close_spider(self):
        self.conn.close()
        self.cursor.close()

bei_bus.py

代码语言:python代码运行次数:0复制
from scrapy import Spider, FormRequest, Request
from urllib.parse import urljoin
from ..Items import BeibusItem

class BeiBusSpider(Spider):
    name = "bei_bus"  # TODO 爬虫的名称,Scrapy 通过该名称识别爬虫
    allowed_domains = ["beijing.8684.cn"]  # TODO 允许爬取的域名
    start_url = "https://beijing.8684.cn"  # TODO 爬虫的起始 URL

    def start_requests(self):
        # TODO 生成请求,爬取前 9 页公交信息
        for page in range(9):
            # TODO 构建每一页的 URL,页码从 1 开始
            url = "{url}/list{page}".format(url=self.start_url, page = (page   1))
            # TODO 使用 FormRequest 发送请求,并指定回调函数
            yield FormRequest(url, callback=self.parse_index)

    def parse_index(self, response):
        # TODO 从响应中提取公交信息的链接
        beijingbus = response.xpath('//div[@class="list clearfix"]/a//@href').extract()
        for href in beijingbus:
            # TODO 将相对链接转换为绝对链接
            url2 = urljoin(self.start_url, href)
            # TODO 发送请求到公交详细信息页面,并指定回调函数
            yield Request(url2, callback=self.parse_detail)

    def parse_detail(self, response):
        # TODO 解析公交详细信息页面,提取所需数据
        bus_name = response.xpath("//div[@class='info']//span/@aria-label").get() or "None"  # 获取公交名称
        bus_type = response.xpath("//h1[@class='title']//a/text()").get() or "None"  # 获取公交类型

        # TODO 获取公交信息列表
        bus_info = response.xpath("//ul[@class='bus-desc']//li/text()").getall()[:-1]
        bus_time = bus_info[0] if len(bus_info) > 0 else "None"  # 获取公交时间
        ticket = bus_info[1] if len(bus_info) > 1 else "None"  # 获取票价

        # TODO 获取公司名称,若不存在则返回 "None"
        gongsi = response.xpath("//ul[@class='bus-desc']//li//a/@title").get() or "None"
        # TODO 获取更新信息,若不存在则返回 "None"
        gengxin = response.xpath("//ul[@class='bus-desc']//li//span/text()").getall()[1] if len(response.xpath("//ul[@class='bus-desc']//li//span/text()").getall()) > 1 else "None"
        # TODO 获取里程信息,若不存在则返回 "None"
        licheng = response.xpath("//div[@class='change-info mb20']//text()").get() or "None"

        # TODO 获取往返路线列表
        wang_fan_load_list = response.xpath("//div[@class='bus-lzlist mb15']//ol//a/text()").getall()
        wang_info = response.xpath("//div[@class='trip']/text()").get() or "None"  # 获取往程信息
        # TODO 获取往程的具体路线,若不存在则返回 "None"
        wang_buff = ",".join(wang_fan_load_list[:(len(wang_fan_load_list) // 2)]) or "None"
        # TODO 获取返程信息
        fan_info = response.xpath("//div[@class='trip']/text()").getall()[1] if len(response.xpath("//div[@class='trip']/text()").getall()) > 1 else "None"
        # TODO 获取返程的具体路线
        fan_buff = ",".join(wang_fan_load_list[(len(wang_fan_load_list) // 2):]) or "None"

        # TODO 创建 BeibusItem 实例用于存储提取的数据
        bus_item = BeibusItem()

        # TODO 将提取的数据存入 bus_item 中
        # TODO bus_item.fields 是 BeibusItem 类中的一个属性,它返回一个字典,字典的键是字段名(字符串形式),值是对应的 Field 实例,每个Field实例就是一个字段名
        for field in bus_item.fields:
            bus_item[field] = eval(field)  # TODO 使用 eval 动态赋值。eval() 函数的作用是将字符串当作 Python 表达式来执行,并返回结果。
            yield bus_item  # TODO 通过 `yield` 返回给 Scrapy, 触发管道,将数据传递给管道

执行

切换至控制台,执行 scrapy crawl bei_bus(指定主程序)

检查结果

  • navicat链接数据库,查看数据

0 人点赞