在数据科学与网络爬虫的世界里,每一行代码都承载着探索未知的使命。本文将带你走进一个精心设计的Python爬虫项目,该项目旨在从信用中国_广东中山: 

aHR0cHM6Ly9jcmVkaXQuenMuZ292LmNuL3B1YmxpY2l0eS9wdWJsaWNpdHl0eXBlL3RvRGF0YUxpc3Q/YWxpYXM9WFpDRiZuYXZQYWdlPTE=

一、项目概述

广东信用网是一个公开的政府网站,提供了大量的行政处罚信息。这些信息对于研究政府监管、企业合规等方面具有重要意义。我们的目标是通过Python爬虫,自动化地抓取这些信息,并进行解析和存储。

二、代码结构

项目的核心类是 CreditGuangDong,它包含了以下几个主要方法:

  1. 初始化方法 (__init__):

    • 初始化基本的URL、请求头、会话对象等。
    • 设置验证码识别工具 ddddocr 和重试机制 retry。
  2. 获取响应 (get_response):

    • 获取并识别验证码。
    • 验证验证码的正确性。
    • 请求列表页数据。
  3. 详情页请求 (detail_response):

    • 请求详情页数据,并处理请求异常。
  4. 详情页解析 (detail_parse):

    • 解析详情页的HTML内容,提取所需信息。
  5. 基础解析 (base_parse):

    • 解析列表页的JSON数据,提取每条记录的ID,并请求对应的详情页。
    • 调用 detail_parse 方法解析详情页内容。
  6. 全站爬取 (all_spider):

    • 循环请求每一页的数据,直到所有页面都爬取完毕。
  7. 主函数 (main):

    • 启动爬虫,调用 all_spider 方法。
三、详细代码解析
1. 初始化方法 (__init__)
def __init__(self):
    self.base_url = "https://credit.zs.gov.cn/publicity/publicitytype/getDataListTpl"
    self.get_yzm_url = "https://credit.zs.gov.cn/system/validatecode/getImgSlider"
    self.check_yzm_url = "https://credit.zs.gov.cn/system/validatecode/sliderCheck"
    self.detail_url = "https://credit.zs.gov.cn/XYGSXZCFDETAIL/"
    self.check_yzm_data = {
        'distance': '1232'
    }
    self.list_data = {
        "shows": " CF_XDR_MC#A, CF_XDR_SHXYM#A, CF_WSH#B, CF_JDRQ$A, ID#A",
        "alias": "XZCF", "zttypefield": "", "page": "1",
        "judgePage": "true", "sgsSelType": "A"
    }
    self.headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
    }
    self.dict_info = {
        '_id': 'ID', 'messageid': '详情页的一个参数(无用)', 'recflag': '详情页的一个参数(无用)',
        'target': '行政相对人名称', 'ajzh': '想着处罚决定书文号', 'judgementdate': '处罚决定日期',
        'sourceurl': '链接', 'sourcename': '数据来源', 'crawldate': '采集时间',
        'content': '行政处罚内容'
    }
    self.session = session()
    self.slide = ddddocr.DdddOcr(show_ad=False, det=False, ocr=False)
    self.total_page = 5
    self.page = 1
  • URL:定义了各个接口的URL。
  • 数据字典:用于存储和映射抓取到的数据。
  • 会话对象:使用 requests.Session 维持会话状态。
  • 验证码识别工具:使用 ddddocr 进行滑块验证码的识别。
2. 获取响应 (get_response)
@retry(tries=5, delay=2, max_delay=100, backoff=2)
def get_response(self):
    """
    1: 先获取验证码图片;
    2: 识别需要移动验证码的距离
    3: 验证识别的验证码距离是否正确, 如果不正确则重复操作1和2
    4: 请求列表页数据
    :return:
    """
    yzm_response = self.session.get(url=self.get_yzm_url, headers=self.headers)
    if yzm_response.status_code == 200:
        yzm_json = yzm_response.json()
        modify_image = base64.b64decode(yzm_json['modifyImg'])
        puzzle_image = base64.b64decode(yzm_json['puzzleImg'])
        # 开始计算其距离
        res = self.slide.slide_match(puzzle_image, modify_image, simple_target=True)
        self.check_yzm_data['distance'] = res['target'][0]
        # 开始验证验证码是否正确
        check_response = self.session.post(url=self.check_yzm_url, headers=self.headers, data=self.check_yzm_data)
        if 'false' in check_response.text:
            print(f"验证码计算出现错误, 正在重新计算中")
            return self.get_response()
        list_response = self.session.post(url=self.base_url, headers=self.headers, data=self.list_data)
        if list_response.status_code == 200:
            return list_response
        raise Exception(f"列表页请求状态码: {list_response.status_code}")
    raise Exception(f"图片验证码请求状态码: {yzm_response.status_code}")
  • 获取验证码:通过 get_yzm_url 获取验证码图片。
  • 识别验证码:使用 ddddocr 计算滑块验证码的移动距离。
  • 验证验证码:通过 check_yzm_url 验证验证码的正确性。
  • 请求列表页:如果验证码正确,请求列表页数据。
3. 详情页请求 (detail_response)
@retry(tries=6, delay=2, max_delay=100, backoff=2)
def detail_response(self, url):
    response = requests.get(url=url)
    if response.status_code == 200:
        return response
    raise Exception(f"详情页请求状态码: {response.status_code}")
  • 请求详情页:通过 requests.get 请求详情页数据。
  • 处理异常:使用 retry 模块处理请求异常,最多重试6次。
4. 详情页解析 (detail_parse)
def detail_parse(self, response, item):
    html = etree.HTML(response.text)
    content = html.xpath('//table[@class="detailTb"]')[0]
    item['ajzh'] = ''.join(content.xpath('.//tr/td[contains(text(), "行政处罚决定书文号")]/following-sibling::td/text()')).strip()
    item['wfxwlx'] = ''.join(content.xpath('.//tr/td[contains(text(), "违法行为类型")]/following-sibling::td/text()')).strip()
    item['cfsy'] = ''.join(content.xpath('.//tr/td[contains(text(), "违法事实")]/following-sibling::td/text()')).strip()
    item['cfyj'] = ''.join(content.xpath('.//tr/td[contains(text(), "处罚依据")]/following-sibling::td/text()')).strip()
    item['cflb'] = ''.join(content.xpath('.//tr/td[contains(text(), "处罚类别")]/following-sibling::td/text()')).strip()
    item['target'] = ''.join(content.xpath('.//tr/td[contains(text(), "行政相对人名称")]/following-sibling::td/text()')).strip()
    item['fddbr'] = ''.join(content.xpath('.//tr/td[contains(text(), "法定代表人")]/following-sibling::td/text()')).strip()
    item['cfjg'] = ''.join(content.xpath('.//tr/td[contains(text(), "处罚内容")]/following-sibling::td/text()')).strip()
    item['cfje'] = ''.join(content.xpath('.//tr/td[contains(text(), "罚款金额")]/following-sibling::td/text()')).strip()
    item['judgementdate'] = ''.join(content.xpath('.//tr/td[contains(text(), "处罚决定日期")]/following-sibling::td/text()')).strip()
    item['department'] = ''.join(content.xpath('.//tr/td[contains(text(), "处罚机关:")]/following-sibling::td/text()')).strip()
    item['content'] = etree.tostring(content, encoding='utf-8', method='html').decode('utf-8').replace('\n', '').replace('\r', '').replace("  ", '')
    item['crawldate'] = datetime.datetime.now()
    print(item)
  • 解析HTML:使用 lxml 解析详情页的HTML内容。
  • 提取信息:通过XPath表达式提取所需的字段。
  • 存储信息:将提取的信息存储到 item 字典中,并打印出来。
5. 基础解析 (base_parse)
def base_parse(self, response):
    json_data = response.json()
    if self.page == 1:
        count = json_data['count']
        total_page = (count // 10) if (count % 10 == 0) else (count // 10 + 1)
        self.total_page = total_page
        print(f"总共有: {self.total_page}页")
    data_list = json_data['dataList']
    for data in data_list:
        try:
            detail_url = f"{self.detail_url}{data['ID']}.html"
            detail_response = self.detail_response(url=detail_url)
        except Exception as e:
            print(f"详情页请求出现错误: {e}")
            continue
        try:
            self.detail_parse(detail_response, item={'url': detail_url})
        except Exception as e:
            print(f"详情页解析出现错误: {e}")
            continue
  • 解析JSON:解析列表页返回的JSON数据。
  • 计算总页数:如果当前是第一页,计算总页数。
  • 请求详情页:遍历每条记录,请求对应的详情页。
  • 解析详情页:调用 detail_parse 方法解析详情页内容。
6. 全站爬取 (all_spider)
def all_spider(self):
    while self.page <= self.total_page:
        try:
            self.list_data['page'] = str(self.page)
            print(f"开始采集第: {self.page}页, 还剩: {self.total_page - self.page}页")
            response = self.get_response()
        except Exception as e:
            print(f"请求出现错误:{e}")
            self.page += 1
            continue
        try:
            self.this_page_max_time = None
            self.base_parse(response)
        except Exception as e:
            print(f"解析失败:{e}")
        self.page += 1
  • 循环请求:循环请求每一页的数据,直到所有页面都爬取完毕。
  • 处理异常:捕获并处理请求和解析过程中可能出现的异常。
7. 主函数 (main)
def main(self):
    self.all_spider()
  • 启动爬虫:调用 all_spider 方法启动爬虫。
四、总结

通过上述代码,我们实现了一个完整的爬虫项目,能够自动化地从广东信用网抓取行政处罚信息。这个项目不仅展示了如何处理滑块验证码,还展示了如何解析复杂的HTML结构,以及如何处理请求和解析过程中的异常。希望这篇文章能帮助你更好地理解和掌握Python爬虫技术,为你的数据科学之旅增添一份力量。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐