selenium+webdriver(POST+GET)国家药监局网爬项目

完整代码链接:https://github.com/JohnWSY/crawlproject-gjypjd

首先确定项目流程:

selenium+webdriver(POST+GET)国家药监局网爬项目_第1张图片

明确需求

从svn download需求文档,明确分工为国家药监局网站全网内容爬取,根据文档与需求进行对接

确定爬取思路

分三级目录
一级目录:共有六大类,40余种小类 正则匹配,进入超链接
selenium+webdriver(POST+GET)国家药监局网爬项目_第2张图片
二级目录:实现翻页,确定穷尽页的停止机制
selenium+webdriver(POST+GET)国家药监局网爬项目_第3张图片
详情页 根据需求,所需爬取的内容都在最后的详情页
selenium+webdriver(POST+GET)国家药监局网爬项目_第4张图片
由以上过程中,发现由二级目录到详情页、以及二级目录的翻页url并不改变,说明网站是ajax请求,即异步javascipt和HTML或XML
所以不能从网页匹配http抓取链接。
只得采取笨方法,通过观察一级目录的网页HTML寻找url的规律,进行拼接。在进入网页的调试页面时,又出现了问题。

网页断点问题

进入网页调试界面后,source显示pause in debugger无法继续点击操作,html卡在标签下,content为乱码
通过点击调试台右上角deactivate breakpoints跳过断点,,然后就可以想点哪里点哪里
selenium+webdriver(POST+GET)国家药监局网爬项目_第5张图片
观察一级目录下的各个分类的content
selenium+webdriver(POST+GET)国家药监局网爬项目_第6张图片
对比点进目录链接后的headers-request url可以发现有一定的相似度,所以试着采用拼接url的方法进行对服务器的访问,可以得到以下页面
selenium+webdriver(POST+GET)国家药监局网爬项目_第7张图片
虽然渲染效果不如原网页,而且点击没有继续链接,但是可以通过正则匹配此页的html字段继续进行下一级的url拼接
但还是由于网页的ajax请求,翻页不跳转,所以需要另寻找方法进行翻页遍历,我们开始观察每一页的headers头部信息

selenium+webdriver(POST+GET)国家药监局网爬项目_第8张图片
我发现头部信息中的form data中的curstart参数就是所在页对应的页码,于是我心生一计,将form data中的所有参数还原为js格式(即点击view source)可以得到一串字符,拼接到下图发现的请求url前面相同的部分也许可以调出页面,实现for循环遍历二级目录页,并最终实现
在这里插入图片描述
在这里插入图片描述

for i in range(1,457):
    url='http://app1.sfda.gov.cn/datasearchcnda/face3/search.jsp?tableId=121&State=1&bcId=152894035121716369704750131820&State=1&curstart='+str(i)+'&State=1&tableName=TABLE121&State=1&viewtitleName=COLUMN1615&State=1&viewsubTitleName=COLUMN1618,COLUMN1616&State=1&tableView=%25E5%2585%25A8%25E5%259B%25BD%25E8%258D%25AF%25E5%2593%2581%25E6%258A%25BD%25E6%25A3%2580&State=1&cid=0&State=1&ytableId=0&State=1&searchType=search&State=1'
            self.crawl(url,cookies=d,callback=self.index_page)

至此,我们回想一级目录进入二级目录的url拼接方式,发现无法继续执行二级目录的翻页,因为二级目录翻页所需的某些参数信息是特定的(eg:tableId),参照需求所需要的详情页信息,决定为一级目录下的40余类分别建立代码文件,也便于后期的数据清洗。
二级目录可通过循环遍历翻页,采用相同的思想,尝试从二级目录的html文件查找最终详情页的访问方式。发现依然可以采用url拼接的方法,遍历访问二级目录页的每条详情页,并保存其text文件。至此,爬取思路完全确定,开始实现代码。

写代码

因为项目统一管理的要求,采用pyspider框架开展爬虫项目,个人感觉pyspider框架的调试功能以及时间参数的设置非常实用

pyspider框架

  1. 时间参数含义 网页
    @every(minutes=24 * 60) 每隔一天爬取一次
    @config(age=10 * 24 * 60 * 60)是设置任务的有效时间为10天,也就是说在10天内不会重复执行
  2. 搞明白优先级的意义priority
    config(priority=2)priority表示爬取的优先级,没有设置默认为0,数字越大,可优先调用

爬取豆瓣电影top250

尤其在学习使用的过程中,尝试爬取豆瓣电影top250尝试总结出电影打分人数与电影评分之间的关系。虽然最后可视化的结果显示没有明显的关系
selenium+webdriver(POST+GET)国家药监局网爬项目_第9张图片

但是在过程中使用了框架内置的css选择器功能,通过web键直接调取页面至框架内,通过css选择器直接插入代码匹配的html标签,非常好用,省去打开浏览器调用调控台的操作。

写项目代码(以全国药品抽检为例)

代码完成后的结果却出现了问题,输出框中的content乱码,查看网页源代码发现网页为gbk2312编码,尝试解码
ASCII 加入GB2312 Unicode 存储传输 UTF-8
'(str) '.encode()用括号内的格式编码 ’ '.decode() 用括号内的编码解码位str
结果却还是不变,这时猛然想起网页的断点问题,不是手动解决这么简单的。查询原因,有一个人提到也许与伪装浏览器的头部缺少cookie参数有关
我在全局声明头部中加入了cookies参数,就可以得到结果,但是之后发现再次为领导演示结果时却为空
debug代码发现,从二级目录并未匹配出相应的详情页url字段,当从新更换一个cookie参数,代码又可以跑通了,所以怀疑是cookie的时效问题

class Handler(BaseHandler):
    
    crawl_config = {'headers':{
               'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
                      }
               }
    
    @every(minutes=24 * 60)
    def on_start(self):
        cookies='JSESSIONID=549B26AF6C4ACAE47F799A3D578B4754.7; FSSBBIl1UgzbN7N82S=xyyKdvxgtlj.G7MvXIyuzgZdAK6TxH_R0wXmYbDoiPab8d4.YHE2_Y5W2A_FpyG4; security_session_verify=3e6bcb698895fc61dce2d3ff3b39501a; FSSBBIl1UgzbN7N82T=2LwZLdYmXTxN7NnD5LAGgMXDFSJO5E2myM0jyg.HLlD28hwoV9X_6K5pCCLmzHJEo784W4KBSvC8pT_KcCoxg9gv3MwjVXW0PyUoKozu7uiCw.918QEumKcV6t_iz4yCSQ6PLUj0aM.oQTSFzWjKCBPieTII_5H7.mrqWVT5MLc1qZFKQJxjPhnHDT3HSDRO5xyAoU3Niaks5zjps9F97foHd0IDrwlB2mDrstJJWbD28jSANzZbY72KEBBY_81eu517P6RA.SgCqP7H_58b4OScuaYwDCZaVAqjrzKbZ17DEPA'
        s1=cookies.replace(';',"','")
        s2=s1.replace('=',"':'")
        s3="{'"+s2+"'}"
        s4=s3.replace(' ','')
        d=eval(s4)
        for i in range(1,457):
            url='http://app1.sfda.gov.cn/datasearchcnda/face3/search.jsp?tableId=121&State=1&bcId=152894035121716369704750131820&State=1&curstart='+str(i)+'&State=1&tableName=TABLE121&State=1&viewtitleName=COLUMN1615&State=1&viewsubTitleName=COLUMN1618,COLUMN1616&State=1&tableView=%25E5%2585%25A8%25E5%259B%25BD%25E8%258D%25AF%25E5%2593%2581%25E6%258A%25BD%25E6%25A3%2580&State=1&cid=0&State=1&ytableId=0&State=1&searchType=search&State=1'
            self.crawl(url,cookies=d,callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        for each in response.doc('a[href^="javascript:commitForECMA(callbackC,"]').items():
            url='http://app1.sfda.gov.cn/datasearchcnda/face3/'+each.attr.href.split("'")[1]
            self.crawl(url,cookies=response.cookies,callback=self.detail_page)
  
    def detail_page(self,response):
        return{
            'url':response.url,
            'content':response.text
        }
  • 查找发现,可以试着使用session机制解决问题
    session机制:采用的是在服务器端保持状态的方案 session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE
    一般一个session可持续20-30分钟,所以还是不解决问题,如果在此时间段内未完成爬取,则结果就不成立;而且,即使短时间COOKIE依然会失效
    cookie: cookie机制采用的是在客户端保持状态的方案 cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session
  • 通过request get 发送请求,得到response.cookies
    这是个死逻辑,没有cookie就跳不过断点,请求得到的cookie不可用,因为它并不完整
    至此,完全放弃pyspider框架,什么统一管理爬取统统放弃,就要做项目里最special的仔

selenium+webdriver 浮出水面

这组搭档简直强大,一手page_source让你无欲无求
首先要pip install selenium 然后再根据电脑使用的浏览器下载对应版本的webdriver,然后就是showtime
pyspider框架在这个项目弃用了,但是我们确定的爬取思路还是正确的,延续使用原有的爬取思路

import re
from selenium import webdriver
from gjypjd.utils import exetcute_sql,if_headless
import pymysql


def main():
    option=None
    #配置文件中开启是否无头,生产阶段关闭
    if if_headless():
        option = webdriver.ChromeOptions()
        option.add_argument(argument='headless')

    for i in range(1, 470):  # 遍历469个一级目录网页
        browser = webdriver.Chrome(chrome_options=option)
        url_1 = 'http://app1.sfda.gov.cn/datasearchcnda/face3/search.jsp?tableId=32&State=1&bcId=152904813882776432084296368957&State=1&curstart='+str(i)+'&State=1&tableName=TABLE32&State=1&viewtitleName=COLUMN302&State=1&viewsubTitleName=COLUMN303,COLUMN299&State=1&tableView=%25E5%259B%25BD%25E4%25BA%25A7%25E8%258D%25AF%25E5%2593%2581%25E5%2595%2586%25E5%2593%2581%25E5%2590%258D&State=1&cid=0&State=1&ytableId=0&State=1&searchType=search&State=1'
        browser.get(url_1)
        s = browser.page_source.replace('amp;', '')
        m = re.findall(r'content.jsp\?tableId=32&tableName=TABLE32&tableView=国产药品商品名&Id=\d+', s, re.M)
        browser.close()

        for j in range(len(m)):
            url_2 = 'http://app1.sfda.gov.cn/datasearchcnda/face3/' + m[j]
            browser = webdriver.Chrome(chrome_options=option)
            browser.get(url_2)
            sql = "insert into t_gcypspm(c_bh, dt_insertTime, c_url, b_content) VALUES (REPLACE(UUID(),\"-\",\"\"), sysdate(), %s,%s)"
            exetcute_sql(sql, [url_2,browser.page_source])
            # pickle.loads(s) 可用该方法将乱码汉字转换
             browser.close()

if __name__ == '__main__':
    main()
  • 这段代码还是有几处要说明的,首先utils.py是全局函数的一个库,当中定义了几个全局函数,例如连接数据库,执行sql语句,if continue判断重复,打印日志等
import configparser
import pymysql
import logging

def logger():
    logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                        level=logging.INFO)
    logger = logging.getLogger('gjypjd')
    return logger

def if_headless():
    cf = configparser.ConfigParser()
    cf.read('./conf/common.ini')

    headless = cf.get('webdriver', 'headless')
    if headless.lower() == 'true':
        return True
    return False

def get_db_conf():
    """
    获取数据库配置信息
    :return:
    """
    cf = configparser.ConfigParser()
    cf.read('./conf/common.ini')

    # print(cf.options("db"))
    db_ip = cf.get('db', 'db_ip')
    db_port = cf.getint('db', 'db_port')
    db_user = cf.get('db', 'db_user')
    db_password = cf.get('db', 'db_password')
    db_name = cf.get('db', 'db_name')
    db_encoding = cf.get('db', 'db_encoding')
    return [db_ip, db_port, db_user, db_password, db_name, db_encoding]

def get_connection():
    conf = get_db_conf()
    return pymysql.connect(host=conf[0], port=conf[1], user=conf[2], password=conf[3], database=conf[4],
                           charset=conf[5])


def exetcute_sql(sql, params):
    """
    执行带有参数的sql
    :param sql:
    :param params:
    :return:
    """
    conn = get_connection()
    try:
        with conn.cursor() as cursor:
            cursor.execute(sql, params)
            logger().info('success execute {}'.format(params[0]))
            conn.commit()
    except:
        logger().error('error execute {}'.format(params[0]))
        conn.rollback()
    finally:
        conn.close()

def select_sql_first(sql, params=None):
    """
    执行带有参数的sql,获取第一条数据
    :param sql:
    :param params:
    :return:
    """
    conn = get_connection()
    try:
        with conn.cursor() as cursor:
            if params:
                cursor.execute(sql, params)
            else:
                cursor.execute(sql)
            data = cursor.fetchone()
            return data
    except:
        logger().error('error execute {}'.format(sql))
        return None
    finally:
        conn.close()

def if_exists(url, tableName):
    """
    检查URL是否存在  如果存在就不进行爬取
    :param url:
    :return:
    """
    sql = 'select count(*) from {} where c_url = \'{}\''.format(tableName, url)
    result = select_sql_first(sql)
    if result[0] == 1:
        return True
    return False
  • if_headless是浏览器的无头调用函数,因为使用selenium+webdriver需要每次打开浏览器进入请求页面,非常占用电脑的资源,所以设置浏览器的无界面状态,效果就是执行中浏览器不再打开,提升爬取的速度。
    代码完成后,需要在数据库中建表来存储爬取到的信息,这又遇到问题
    通过uuid, datetime, url, content, json关键字建表,其中contetnt格式为blob(二进制长文本)
/*
 Navicat Premium Data Transfer

 Source Server : 172.18.15.130_3306
 Source Server Type : MySQL
 Source Server Version : 50726
 Source Host : 172.18.15.130:3306
 Source Schema : webdriverdb

 Target Server Type : MySQL
 Target Server Version : 50726
 File Encoding : 65001

 Date: 19/06/2019 14:57:40
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_jkypspm
-- ----------------------------
DROP TABLE IF EXISTS `t_jkypspm`;
CREATE TABLE `t_jkypspm` (
  `c_bh` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '编号',
  `dt_insertTime` datetime(0) DEFAULT NULL COMMENT '插入时间',
  `c_url` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据url',
  `b_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '内容',
  `c_json` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据对应的json',
  PRIMARY KEY (`c_bh`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '进口药品商品名' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

但是在数据库的结果中,contetnt汉字部分乱码,因为blob格式为二进制,所以使用pickle模块dumps()函数对其进行序列化,并在数据清洗中使用loads()方法反序列化可得到汉字,但是有些麻烦,跟负责人沟通后,改用text格式存储page_source乱码问题不再出现,并且决定在代码中加入json解析直接得到需求对应的字段,省去后期的数据清洗

\# 化妆品行政许可检验机构

import pickle
import re
from selenium import webdriver
from gjypjd.utils import exetcute_sql,if_headless,if_exists
import json

def main():
    option=None
    #配置文件中开启是否无头,生产阶段关闭
    if if_headless():
        option = webdriver.ChromeOptions()
        option.add_argument(argument='headless')

    for i in range(1, 4):  # 遍历3个一级目录网页
        browser = webdriver.Chrome(chrome_options=option)
        url_1 = 'http://app1.sfda.gov.cn/datasearchcnda/face3/search.jsp?tableId=108&State=1&bcId=152904558282171636476541922479&State=1&curstart='+str(i)+'&State=1&tableName=TABLE108&State=1&viewtitleName=COLUMN1416&State=1&viewsubTitleName=COLUMN1421&State=1&tableView=%25E5%258C%2596%25E5%25A6%2586%25E5%2593%2581%25E8%25A1%258C%25E6%2594%25BF%25E8%25AE%25B8%25E5%258F%25AF%25E6%25A3%2580%25E9%25AA%258C%25E6%259C%25BA%25E6%259E%2584&State=1&cid=0&State=1&ytableId=0&State=1&searchType=search&State=1'
        browser.get(url_1)
        s = browser.page_source.replace('amp;', '')
        m = re.findall(r'content.jsp\?tableId=108&tableName=TABLE108&tableView=化妆品行政许可检验机构&Id=\d+', s, re.M)
        browser.close()

        for j in range(len(m)):
            url_2 = 'http://app1.sfda.gov.cn/datasearchcnda/face3/' + m[j]
            if if_exists(url_2, 't_hzpxzxkjyjg'):
                continue
            browser = webdriver.Chrome(chrome_options=option)
            browser.get(url_2)
            sql = "insert into t_hzpxzxkjyjg(c_bh, dt_insertTime, c_url, b_content, c_json) VALUES (REPLACE(UUID(),\"-\",\"\"), sysdate(), %s,%s,%s)"
            exetcute_sql(sql, [url_2, browser.page_source, parse2json(browser.page_source)])

            # pickle.loads(s) 可用该方法将乱码汉字转换
            browser.close()

def parse2json(html):
    """
    检验机构名称jyjgmc
    联系地址lxdz
    联系人lxr
    联系电话lxdh
    传真cz
    机构类别jglb
    检验项目jyxm
    :return:json
    """
    # 初始化,避免取不到的情况下为空值
    result_json = dict()
    # 批准文号
    reg_dict = dict()
    reg_dict['jyjgmc'] = r"检验机构名称\s*(.*)"
    reg_dict['lxdz'] = r"联系地址\s*(.*)"
    reg_dict['lxr'] = r"联系人\s*(.*)"
    reg_dict['lxdh'] = r"联系电话\s*(.*)"
    reg_dict['cz'] = r"传真\s*(.*)"
    reg_dict['jglb'] = r"机构类别\s*(.*)"
    reg_dict['jyxm'] = r"检验项目\s*(.*)"

    for i, v in reg_dict.items():
        reg_search = re.search(v, html)
        if reg_search is not None:
            result_json[i] = reg_search.group(1)
        else:
            result_json[i] = ''
    return json.dumps(result_json, ensure_ascii=False)

if __name__ == '__main__':
    main()

至此,轰轰烈烈的项目就基本结束了,剩下的只是重复性的调试一级目录内的40余种小类,查看数据库中的结果是否正确
selenium+webdriver(POST+GET)国家药监局网爬项目_第10张图片

啪啪打脸的结尾

项目并没有结束,在吹响胜利号角的时候,发现了新的问题,那就是post!
核对需求发现有五处详情页列表的问题,跟原模式不太一样了,所以要重新构思,不过有了前面代码的铺垫,还是有点门路的
PS:这里要批评一下随意的需求!!!每当有问题,去跟需求核对,得到的解释大多是,这个不需要了???
WTF你知不知道,你看似一个简单的东西,很可能需要我们处理很久,解决很多的难点!!!尤其对于我这种实习生!太气人了!
所以需求必须做好,客户要的不能少,技术做的不能多,如果说需求做不到精确,团队的效率会下降,同时团队凝聚力也会受到很大的影响。
说回来项目:
三个列表页,有两个由我完成,剩下的一个由新来的实习生完成,不得不说赵薇喊这个人真的在这没经历敲代码的痛点,她的痛点主要集中在装软件
先说第一个,化妆品生产许可获证企业
selenium+webdriver(POST+GET)国家药监局网爬项目_第11张图片
首先这是一个目录页,而且需要翻页,那么F12查看网页源码,发现网页为post请求,ajax通信
通过post man 发送post请求,得到response响应
selenium+webdriver(POST+GET)国家药监局网爬项目_第12张图片

综上,思路清晰,直接代码

# -*- coding: utf-8 -*-
from seleniumrequests import Chrome
import json
from selenium import webdriver
from gjypjd.utils import  *

def main():
    option = None
    mysql_db = DataBase()
    # 配置文件中开启是否无头,生产阶段关闭
    if if_headless():
        option = webdriver.ChromeOptions()
        option.add_argument(argument='headless')
        option.add_argument('--no-sandbox')

    for i in range(1, 330):
        browser = Chrome(chrome_options=option)
        url='http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList&on=true&page=' + str(
                                  i) + '&pageSize=15&productName=&conditionType=1&applyname=&applysn'
        res = browser.request('post', url)
        # print(res.text)
        res1 = json.loads(res.content)['list']
        # print(res1)
        browser.close()
        for j in range(len(res1)):
            sql = "insert into t_hzpscxkhzqy_lbsj(c_bh, dt_insertTime, c_url, b_content, c_json,c_page) VALUES (REPLACE(UUID(),\"-\",\"\"), sysdate(), %s,%s,%s,%s)"
            mysql_db.exetcute_sql(sql, [url, res.content, parse(res1[j]),
                                        str(i) + '_' + str(j + 1)])

def parse(dic):
    """
    企业名称qymc
    许可证编号xkzbh
    发证机关fzjg
    有效期至yxqz
    发证日期fzrq
    """

    reg_dict = dict()
    reg_dict['qymc'] = dic['EPS_NAME']
    reg_dict['xkzbh'] = dic['PRODUCT_SN']
    reg_dict['fzjg'] = dic['QF_MANAGER_NAME']
    reg_dict['yxqz'] = dic['XK_DATE']
    reg_dict['fzrq'] = dic['XC_DATE']

    return json.dumps(reg_dict, ensure_ascii=False)


if __name__ == '__main__':
    main()

直接得到要求的匹配字段,继续来看,我们需要爬取企业详情页信息,模式跟一级目录也相同

# -*- coding: utf-8 -*-
from seleniumrequests import Chrome
import json
from selenium import webdriver
from gjypjd.utils import  *

def main():
    option = None
    mysql_db = DataBase()
    # 配置文件中开启是否无头,生产阶段关闭
    if if_headless():
        option = webdriver.ChromeOptions()
        option.add_argument(argument='headless')
        option.add_argument('--no-sandbox')

    for i in range(1, 330):
        browser = Chrome(chrome_options=option)
        url_1='http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList&on=true&page=' + str(
                                  i) + '&pageSize=15&productName=&conditionType=1&applyname=&applysn'
        res1 = browser.request('post', url_1)
        # print(res.text)
        res1= json.loads(res1.content)['list']
        # print(res1)
        browser.close()
        for j in range(len(res1)):
            browser = Chrome(chrome_options=option)
            url_2='http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById&id='+res1[j]['ID']
            res2 = browser.request('post', url_2)
            res3 =json.loads(res2.content)
            # print(res3)
            browser.close()
            sql = "insert into t_hzpscxkzhzqy_xkxq(c_bh, dt_insertTime, c_url, b_content, c_json,c_page) VALUES (REPLACE(UUID(),\"-\",\"\"), sysdate(), %s,%s,%s,%s)"
            mysql_db.exetcute_sql(sql, [url_2, res2.content, parse(res3),
                                        str(i) + '_' + str(j + 1)])

def parse(dic):
    """
    企业名称qymc
    许可证编号xkzbh
    许可项目xkxm
    企业住所qyzs
    生产地址scdz
    社会信用代码shxydm
    法定代表人fddbr
    企业负责人qyfzr
    质量负责人zlfzr
    发证机关fzjg
    签发人qfr
    日常监督管理机构rcjdgljg
    日常监督管理人员rcjdglry
    有效期至yxqz
    发证日期fzrq
    状态zt
    投诉举报电话tsjbdh

    """

    reg_dict = dict()
    reg_dict['qymc'] = dic['epsName']
    reg_dict['xkzbh'] = dic['productSn']
    reg_dict['xkxm'] = dic['certStr']
    reg_dict['qyzs'] = dic['epsAddress']
    reg_dict['scdz'] = dic['epsProductAddress']
    reg_dict['shxydm'] = dic['businessLicenseNumber']
    reg_dict['fddbr'] = dic['legalPerson']
    reg_dict['qyfzr'] = dic['businessPerson']
    reg_dict['zlfzr'] = dic['qualityPerson']
    reg_dict['fzjg'] = dic['qfManagerName']
    reg_dict['qfr'] = dic['xkName']
    reg_dict['rcjdgljg'] = dic['rcManagerDepartName']
    reg_dict['rcjdglry'] = dic['rcManagerUser']
    reg_dict['yxqz'] = dic['xkDate']
    reg_dict['fzrq'] = dic['xkDateStr']
    reg_dict['zt'] = '正常'
    reg_dict['jbdh'] = '12331'

    return json.dumps(reg_dict, ensure_ascii=False)


if __name__ == '__main__':
    main()

很简单,没说什么问题

新的问题产生了,进口非特化妆品备案管理

虽然同样为post请求,但是postman给出的response却是error,尝试传入参数来解决,发现postman的bug
selenium+webdriver(POST+GET)国家药监局网爬项目_第13张图片
尝试直接在代码中传入参数,发送post请求,传入headers和params,得到了JSON格式的响应
同时,在敲代码过程中,发现了数据动态更新,每天都会有批量数据的传入,所以翻页问题要传入动态参数,通过先发送post请求得到page参数

# -*- coding: utf-8 -*-
# 进口非特殊用途化妆品备案信息-备案详情
from seleniumrequests import Chrome
import json
from selenium import webdriver
import re
from gjypjd.utils import *


def main():
    option = None
    mysql_db = DataBase()
    # 配置文件中开启是否无头,生产阶段关闭
    if if_headless():
        option = webdriver.ChromeOptions()
        option.add_argument(argument='headless')
        option.add_argument('--no-sandbox')


    browser = Chrome(chrome_options=option)
    url_1 = 'http://cpnp.nmpa.gov.cn/province/webquery/wq.do?method=query&querytype=productname&pfid=&content=&dataPage=0&perPage=15&allRows=8084&order='
    response = browser.request('post', url_1)
    response1 = json.loads(response.content)['pageBean']
    page = response1['allPage']
    browser.close()


    for i in range(0, page):
        browser = Chrome(chrome_options=option)
        url_1='http://cpnp.nmpa.gov.cn/province/webquery/wq.do?method=query&querytype=productname&pfid=&content=&dataPage='+str(i)+'&allPage=539&perPage=15&allRows=8084&order='
        res1 = browser.request('post', url_1)
        res1= json.loads(res1.content)['list']
        browser.close()
        for j in range(len(res1)):
            browser = Chrome(chrome_options=option)
            url_2='http://cpnp.nmpa.gov.cn/province/webquery/wq.do?'
            params = {'method': 'show','id':res1[j]['id']}
            headers={
                    'Accept':'application/json, text/javascript, */*; q=0.01',
                    'Accept-Encoding':'gzip, deflate',
                    'Accept-Language':'zh-CN,zh;q=0.9',
                    'Connection':'keep-alive',
                    'Content-Length': '31',
                    'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
                    'Cookie':'_gscu_515232071=60234697wnhvr115; _gscbrs_515232071=1; JSESSIONID=DA4CEE8CEE0F521678039F251D0A32AD',
                    'Host':'cpnp.nmpa.gov.cn',
                    'Origin':'http://cpnp.nmpa.gov.cn',
                    'Referer':'http://cpnp.nmpa.gov.cn/province/webquery/show.jsp?id=50BF34D2A36759BA',
                    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36',
                    'X-Requested-With':'XMLHttpRequest'
                    }
            res2 = browser.request('post', url_2, data=params, headers=headers)
            browser.close()
            sql = "insert into t_jkftsythzpbaxx_baxq(c_bh, dt_insertTime, c_url, b_content, c_json,c_page) VALUES (REPLACE(UUID(),\"-\",\"\"), sysdate(), %s,%s,%s,%s)"
            mysql_db.exetcute_sql(sql, [url_2, res2.text, parse(json.loads(res2.text)),
                                        str(i+1) + '_' + str(j+1)])

def parse(dic):
    """
    产品名称cpmc
    产品英文名称cpywm
    备案编号babh
    备案日期barq
    生产企业名称(中文)scqymc_zw
    生产企业名称(英文)scqymc_yw
    生产企业地址scqydz
    境内负责人名称jnfzrmc
    境内负责人地址jnfzrdz
    生产国(地区)scg_dq
    进口省份jksf
    成分cf
    备注bz
    备案资料核查bazlsc
    历史ls
    技术要求jsyq
    产品设计包装平面图cpsjbzpmt
    产品中文标签cpzwbq
    产品上市包装立体图cpssbzltt
    """
    s = re.findall('id=([0-9|A-Z]{16})', dic['preview'])
    reg_dict = dict()
    reg_dict['cpmc'] = dic['productname']
    reg_dict['cpywm'] = dic['productnameen']
    reg_dict['babh'] = dic['passno']
    reg_dict['barq'] = dic['passdate']
    reg_dict['scqymc_zw'] = dic['enterprise']
    reg_dict['scqymc_yw'] = dic['enterpriseen']
    reg_dict['scqydz'] = dic['enterpriseaddressen']
    reg_dict['jnfzrmc'] = dic['internalunitname']
    reg_dict['jnfzrdz'] = dic['internalunitaddr']
    reg_dict['scg_dq'] = dic['Country']
    reg_dict['jksf'] = dic['jksf']
    reg_dict['cf'] = dic['cf']
    reg_dict['bz'] = dic['memo']
    reg_dict['bazlsc'] = dic['CheckupResult']
    reg_dict['ls'] = dic['hisList']
    reg_dict['jsyq'] = 'http://cpnp.nmpa.gov.cn/province/webquery/wq.do?method=jsyq&id='+s[0]
    reg_dict['cpsjbzpmt'] = 'http://cpnp.nmpa.gov.cn/province/webquery/preview.jsp?id='+s[1]
    reg_dict['cpzwbq'] = 'http://cpnp.nmpa.gov.cn/province/webquery/preview.jsp?id='+s[2]
    reg_dict['cpssbzltt'] = 'http://cpnp.nmpa.gov.cn/province/webquery/preview.jsp?id='+s[3]

    return json.dumps(reg_dict, ensure_ascii=False)


if __name__ == '__main__':
    main()

基本项目到此真的结束,剩下的就是根据需求反馈,调整未得到的数据,做一个一段时间的维护

你可能感兴趣的:(爬虫)