@TOC

1 需要起源

  • 获取网页指定区域数据,并进行保留;
  • 简略说就是pa chong的需要了。

2 需要细节

留神:请文化上网,本文仅作为学习用。讲述的是思路和办法,所以对被测试网站要害数据进行暗藏。如有须要,可学习思路后自行找测试对象进行学习。

  • 某网站,进入后如下,有很多数据分类:

  • 进入某个分类后有很多小分类,如电阻器中的页面:
  • 而每个小类又有很多数据,那咱们就是要把这些数据下载下来进行保留:

3 设计剖析

依据以上【需要细节】,咱们曾经大略明确须要做啥,就是要下载一个大的分类下的小类中的内容:

  • 要申请对应页面数据,那咱们须要用到的requests.get办法;
  • 申请完数据后,要获取对应元素的html,要用到etree.HTMLtree.xpath办法;
  • 因为这些大类或小类,其实实质上都是不同的链接,从页面看咱们可能须要获取a标签,那么须要应用BeautifulSoup进行页面解析;
  • 下载下来的数据,咱们要进行保留到html格局的文件中,那咱们要用到根本的数据写入,比方openwrite办法;
  • 想把下载下来的html原格局保留到excel中,那须要对htmlexcel格局进行解析,须要应用pandas进行解决;
  • 这个两头过程中,须要对文件和门路进行解决,所以还须要用到Path办法;
  • 最初咱们把脚本打包成exe不便运行和应用,那须要用到打包工具Pyinstaller解决。

4 技术栈

  • 从【3 设计剖析】来看,咱们须要用到以下工具环境。
工具版本用处
PythonV3.7.0脚本设计
beautifulsoup4V4.11.1html页面数据解析
lxmlV4.6.3etree.HTMLtree.xpath获取对应元素的html
pandasV1.1.5excel数据处理
requestsV2.24.0页面数据申请
  • 复制以下内容命名为 requirements.txt,间接应用pip install -r requirements.txt 即可装置须要的包;
beautifulsoup4==4.11.1lxml==4.6.3pandas==1.1.5requests==2.24.0

5 设计实现

  • 先引入所有须要的包:
import requestsfrom lxml import etreefrom bs4 import BeautifulSoupimport pandasimport osimport timefrom pathlib import Path
  • 创立基类Tools:
class Tools(object):    """公共办法(工具)作为基类被后续调用"""

5.1 封装公共办法类Tools

5.1.1 封装数据申请办法get_category

  • 创立办法get_category,传入四个参数:
    def get_category(self,                     curt_url,                     curt_xpath,                     curt_list,                     curt_headers):        """        申请办法封装        :param curt_url: 申请地址        :param curt_xpath: 对应table xpath        :param curt_list: 寄存列表        :param curt_headers: 申请头        :return: 无返回        """
  • 为什么要这么做?为了防止代码冗余,后续有很多中央用到数据申请和获取,所以进行了封装。而传入的四个参数,根本是变动的,所以用到时候,传入须要的参数即可;
  • 在每次申请前加个提早:time.sleep(1),防止申请太过频繁;
  • 应用requests.get办法,获取指标地址数据,其中要退出两个参数,次要防止申请报SSl谬误:
res = requests.get(curt_url,                   verify=False,                   headers=curt_headers)    
  • 应用etree.HTML办法返回的数据进行html转换:
tree = etree.HTML(res.content)  
  • 应用tree.xpath办法获取该页面中指定元素的内容:
div = tree.xpath(curt_xpath) 
  • 应用以下办法进行格局转换,获取的数据是byte字节,转换成str类型;
div_str = etree.tostring(div[0]) div_str1 = str(div_str, "UTF-8")  
  • 应用BeautifulSoup办法解析页面html,获取a标签的所有链接内容,就是大类或小类的名字对应的链接了;
soup = BeautifulSoup(div_str1)       for k in soup.find_all('a'):                       curt_list.append(k['href']) 
  • get_category办法源码:
    def get_category(self,                     curt_url,                     curt_xpath,                     curt_list,                     curt_headers):        """        申请办法封装        :param curt_url: 申请地址        :param curt_xpath: 对应table xpath        :param curt_list: 寄存列表        :param curt_headers: 申请头        :return: 无返回        """        time.sleep(1)        res = requests.get(curt_url,                           verify=False,                           headers=curt_headers)        # 接口数据申请办法        tree = etree.HTML(res.content)                  # 获取返回数据的内容        div = tree.xpath(curt_xpath)                    # 获取以后页面须要的table xpath对应的内容        div_str = etree.tostring(div[0])                # 格局转换        div_str1 = str(div_str, "UTF-8")                # byte转为str        # print(div_str1)        soup = BeautifulSoup(div_str1)                  # BeautifulSoup解析页面html        for k in soup.find_all('a'):                    # 获取a标签            curt_list.append(k['href'])

5.1.2 封装html数据写入办法write_html

  • 就是把以上获取的内容存入html格局的文件中;
  • 这个简略,间接上代码:
    def write_html(self, file, txt):        """        公共办法:把获取的数据写入文本内容到文件【html格局】        :param file: 文件名        :param txt: 文本内容        :return: 返回胜利或失败        """        try:            with open(file, 'w', encoding='utf-8') as f:                f.write(txt)                time.sleep(3)                f.close()            return f"{file}写入: 胜利"        except:            return f"{file}写入: 失败"

5.1.3 封装html转excel办法html_to_excel

  • 简略说,就是把html文件转换成excel格局;
  • 传入五个参数:
    def html_to_excel(self,                      base_dir,                      big_dir,                      small_dir,                      full_path,                      new_file_path):        """        将html文件转换成excel格局的文件        :param base_dir: 文件寄存基地址,默认脚本的上一层目录        :param big_dir: 大类目录        :param small_dir: 小类目录        :param excel_dir: 寄存excel目录        :param sheet_n: 寄存sheet的名称=小类        :param full_path: 所有sheet合并目录        :param new_file_path: 最终合并的某个小类的excel        :return:无返回        """
  • 大略思路是:
①关上指定目录下的html格式文件;
②循环遍历所有的html格式文件,应用pandas.read_html进行数据读取;
③应用pandas.ExcelWriter办法写入excel;
④写入excel后是每个html寄存在每个sheet中;
⑤合并所有的sheet为一个excel。
  • 间接上代码:
    def html_to_excel(self,                      base_dir,                      big_dir,                      small_dir,                      full_path,                      new_file_path):        """        将html文件转换成excel格局的文件        :param base_dir: 文件寄存基地址,默认脚本的上一层目录        :param big_dir: 大类目录        :param small_dir: 小类目录        :param excel_dir: 寄存excel目录        :param sheet_n: 寄存sheet的名称=小类        :param full_path: 所有sheet合并目录        :param new_file_path: 最终合并的某个小类的excel        :return:无返回        """        excel_dir = base_dir + "\\" + big_dir + "\\" + small_dir + "\\"        sheet_n = small_dir        # sheet_n = "1-陶瓷电容器"        os.chdir(excel_dir)        for filename in os.listdir(excel_dir):            print(filename)            try:                with open(excel_dir + filename, 'rb') as f:                    df = pandas.read_html(f.read(), header=1, encoding='utf-8')                    bb = pandas.ExcelWriter(excel_dir + filename + ".xlsx")                    df[0].to_excel(bb, index=False)                    bb.close()            except Exception as e:                print("异样:" + e)        time.sleep(3)        workbook = pandas.ExcelWriter(full_path)        folder_path = Path(excel_dir)        file_list = folder_path.glob('*.xlsx*')        for i in file_list:            stem_name = i.stem            data = pandas.read_excel(i, sheet_name=0)            data.to_excel(workbook, sheet_name=stem_name, index=False)        time.sleep(2)        workbook.save()        workbook.close()        time.sleep(2)        data2 = pandas.read_excel(full_path, sheet_name=None)        data3 = pandas.concat(data2, ignore_index=True)        # new_file_path = "合并.xlsx"        data3.to_excel(new_file_path, sheet_name=sheet_n, index=False)

5.2 两个全局变量寄存获取的数据名称

category_list = []  # 寄存所有大类category_list_small = []  # 寄存所有小类

5.3 创立数据处理和获取类DataBase

5.3.1 初始化类

    def __init__(self):        # self.tools = Tools()        self.url = 'xxxx'                                 # 指标网站        self.headers = {'Connection': 'close'}            # 申请头,防止ssl报错        # self.big_num = 3                                # 第几个大类,从0开始        # self.small_num = 0                              # 第几个小类,从0开始        self.net_xpath = '/html/body/div[5]/div/div[2]'   # 网站所有大类的table xpath        self.xpath_big = ['/html/body/div[3]/div[2]']     # 对应大类中的小类的table xpath        self.xpath_small = ['/html/body/div[4]/div']      # 对应小类的内容table xpath

5.3.2 获取所有大类名称存入列表

    def get_big_category(self):        """获取网站中所有的类别,寄存列表中"""        self.get_category(self.url,                          self.net_xpath,                          category_list,                          self.headers)        print(f"1========={category_list}")

5.3.3 获取所有大类中小类的名称存入列表

    def get_small_category(self, big_num):        """获取某个大类中小类所有的类别,寄存列表中"""        self.get_category(f'{self.url}{category_list[big_num]}',                          self.xpath_big[0],                          category_list_small,                          self.headers)        print(f"获取的大类是: {category_list[big_num]} ,如下:")

5.3.4 获取小类中页面的内容

    def get_small_content(self, i, small_num):        """获取小类中所有内容"""        print(f"获取的大类对应的小类是:{category_list_small[small_num]}")        time.sleep(1)        url_1 = f'{self.url}{category_list_small[small_num]}?page={i}'        print(f"申请的小类的域名为:{url_1}")        res = requests.get(url_1, verify=False, headers=self.headers)        tree = etree.HTML(res.content)        div = tree.xpath(self.xpath_small[0])        div_str = etree.tostring(div[0])        div_str1 = str(div_str, "UTF-8")        time.sleep(2)        return div_str1

5.4 办法调用main设计

5.4.1 输入输出规定

def main():    print("*" * 20)    print("在运行前请所相熟下规定:\n"          "1、依照网页显示,大类名称输出 数字-大类名称,如1-电阻器\n"          "2、小类名称输出 数字-小类名称,如1-固定电阻器\n"          "3、大小类前边的数字示意第几个\n"          "4、如果输错不做判断,只是寄存的门路须要本人查找,倡议一次性输出正确\n"          "5、!!!!!程序执行过程请勿敞开任何窗口!!!!!")    print("*" * 20)

5.4.2 对以后脚本门路进行解决

    base_file = os.path.dirname(os.path.abspath("test_database_final.py"))    print("#" * 20)    print("程序将开始执行,请稍后......\n"          "程序曾经启动~\n"          f"程序启动目录为:{base_file}\n"          "初始化数据......")    print("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n"          "1-电阻器        2-连接器          3-连接器支架      4-电容器                 5-振荡器              \n"          "6-晶体/谐振器    7-电源电路        8-开关           9-传感器/温度传感器        10-光电               \n"          "11-光纤         12-二极管         13-电路爱护      14-存储                  15-信号电路            \n"          "16-电感器       17-端子           18-插座          19-微控制器和处理器       20-射频和微波          \n"          "21-逻辑         22-晶体管         23-继电器        24-转换器                25-过滤器              \n"          "26-触发安装      27-RC网络        28-可编程逻辑     29-电信电路              30-驱动程序和接口       \n"          "31-放大器电路    32-耐热撑持安装    33-变压器        34-生产电路              35-电池               \n"          "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")

5.4.3 从键盘输入要获取的数据信息

    big = input("请输出大类的名称(如1-电阻器): ")    small = input("请输出小类的名称(如1-固定电阻器): ")    num = int(input("请输出小类的页数(如50,须要从网站查看): "))    b_n = int(input("请输出该大类对应的序号,共35个大类,从左到右数从0开始,比方0: "))    m_n = int(input("请输出该小类对应的序号,从0开始,比方0: "))    print(f"通过输出,咱们要获取的数据为:第{b_n + 1}个大类中的第{m_n + 1}个小类\n"          f"即:{big}中的{small}")

5.4.4 数据调用

    data_base = DataBase()    data_base.get_big_category()    data_base.get_small_category(b_n)

5.4.5 循环换入每页中的数据

    for i in range(1, num+1):        get_content = data_base.get_small_content(i, m_n)        print(f"第{i}次获取:获取的数据开始写入文件,文件名为:第{i}页.html")        file = f"{base_file}\\{big}\\{small}"        if os.path.exists(file) is False:            os.makedirs(file)        data_base.write_html(file=f"{file}\\第{i}页.html",                             txt=get_content)        time.sleep(1)

5.4.6 获取的数据合并存入最终的excel

    data_base.html_to_excel(base_file,                            big,                            small,                            f"{small}sheet.xlsx",                            f"{small}.xlsx")

5.4.7 main办法源码

def main():    print("*" * 20)    print("在运行前请所相熟下规定:\n"          "1、依照网页显示,大类名称输出 数字-大类名称,如1-电阻器\n"          "2、小类名称输出 数字-小类名称,如1-固定电阻器\n"          "3、大小类前边的数字示意第几个\n"          "4、如果输错不做判断,只是寄存的门路须要本人查找,倡议一次性输出正确\n"          "5、!!!!!程序执行过程请勿敞开任何窗口!!!!!")    print("*" * 20)    base_file = os.path.dirname(os.path.abspath("test_database_final.py"))    print("#" * 20)    print("程序将开始执行,请稍后......\n"          "程序曾经启动~\n"          f"程序启动目录为:{base_file}\n"          "初始化数据......")    print("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n"          "1-电阻器        2-连接器          3-连接器支架      4-电容器                 5-振荡器              \n"          "6-晶体/谐振器    7-电源电路        8-开关           9-传感器/温度传感器        10-光电               \n"          "11-光纤         12-二极管         13-电路爱护      14-存储                  15-信号电路            \n"          "16-电感器       17-端子           18-插座          19-微控制器和处理器       20-射频和微波          \n"          "21-逻辑         22-晶体管         23-继电器        24-转换器                25-过滤器              \n"          "26-触发安装      27-RC网络        28-可编程逻辑     29-电信电路              30-驱动程序和接口       \n"          "31-放大器电路    32-耐热撑持安装    33-变压器        34-生产电路              35-电池               \n"          "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")    big = input("请输出大类的名称(如1-电阻器): ")    small = input("请输出小类的名称(如1-固定电阻器): ")    num = int(input("请输出小类的页数(如50,须要从网站查看): "))    b_n = int(input("请输出该大类对应的序号,共35个大类,从左到右数从0开始,比方0: "))    m_n = int(input("请输出该小类对应的序号,从0开始,比方0: "))    print(f"通过输出,咱们要获取的数据为:第{b_n + 1}个大类中的第{m_n + 1}个小类\n"          f"即:{big}中的{small}")    data_base = DataBase()    data_base.get_big_category()    data_base.get_small_category(b_n)    for i in range(1, num+1):        get_content = data_base.get_small_content(i, m_n)        print(f"第{i}次获取:获取的数据开始写入文件,文件名为:第{i}页.html")        file = f"{base_file}\\{big}\\{small}"        if os.path.exists(file) is False:            os.makedirs(file)        data_base.write_html(file=f"{file}\\第{i}页.html",                             txt=get_content)        time.sleep(1)    data_base.html_to_excel(base_file,                            big,                            small,                            f"{small}sheet.xlsx",                            f"{small}.xlsx")

5.5 主程序调用

if __name__ == "__main__":    main()    input('Press Enter to exit…')

6 残缺源码

# -*- coding:utf-8 -*-# 作者:NoamaNelson# 日期:2022/10/11# 文件名称:test_database_final.py# 作用:xxx# 博客:https://blog.csdn.net/NoamaNelsonimport requestsfrom lxml import etreefrom bs4 import BeautifulSoupimport pandasimport osimport timefrom pathlib import Pathclass Tools(object):    """公共办法(工具)作为基类被后续调用"""    def get_category(self,                     curt_url,                     curt_xpath,                     curt_list,                     curt_headers):        """        申请办法封装        :param curt_url: 申请地址        :param curt_xpath: 对应table xpath        :param curt_list: 寄存列表        :param curt_headers: 申请头        :return: 无返回        """        time.sleep(1)        res = requests.get(curt_url,                           verify=False,                           headers=curt_headers)        # 接口数据申请办法        tree = etree.HTML(res.content)                  # 获取返回数据的内容        div = tree.xpath(curt_xpath)                    # 获取以后页面须要的table xpath对应的内容        div_str = etree.tostring(div[0])                # 格局转换        div_str1 = str(div_str, "UTF-8")                # byte转为str        # print(div_str1)        soup = BeautifulSoup(div_str1)                  # BeautifulSoup解析页面html        for k in soup.find_all('a'):                    # 获取a标签            curt_list.append(k['href'])                 # 循环获取href链接    def write_html(self, file, txt):        """        公共办法:把获取的数据写入文本内容到文件【html格局】        :param file: 文件名        :param txt: 文本内容        :return: 返回胜利或失败        """        try:            with open(file, 'w', encoding='utf-8') as f:                f.write(txt)                time.sleep(3)                f.close()            return f"{file}写入: 胜利"        except:            return f"{file}写入: 失败"    def html_to_excel(self,                      base_dir,                      big_dir,                      small_dir,                      full_path,                      new_file_path):        """        将html文件转换成excel格局的文件        :param base_dir: 文件寄存基地址,默认脚本的上一层目录        :param big_dir: 大类目录        :param small_dir: 小类目录        :param excel_dir: 寄存excel目录        :param sheet_n: 寄存sheet的名称=小类        :param full_path: 所有sheet合并目录        :param new_file_path: 最终合并的某个小类的excel        :return:无返回        """        excel_dir = base_dir + "\\" + big_dir + "\\" + small_dir + "\\"        sheet_n = small_dir        # sheet_n = "1-陶瓷电容器"        os.chdir(excel_dir)        for filename in os.listdir(excel_dir):            print(filename)            try:                with open(excel_dir + filename, 'rb') as f:                    df = pandas.read_html(f.read(), header=1, encoding='utf-8')                    bb = pandas.ExcelWriter(excel_dir + filename + ".xlsx")                    df[0].to_excel(bb, index=False)                    bb.close()            except Exception as e:                print("异样:" + e)        time.sleep(3)        workbook = pandas.ExcelWriter(full_path)        folder_path = Path(excel_dir)        file_list = folder_path.glob('*.xlsx*')        for i in file_list:            stem_name = i.stem            data = pandas.read_excel(i, sheet_name=0)            data.to_excel(workbook, sheet_name=stem_name, index=False)        time.sleep(2)        workbook.save()        workbook.close()        time.sleep(2)        data2 = pandas.read_excel(full_path, sheet_name=None)        data3 = pandas.concat(data2, ignore_index=True)        # new_file_path = "合并.xlsx"        data3.to_excel(new_file_path, sheet_name=sheet_n, index=False)category_list = []  # 寄存所有大类category_list_small = []  # 寄存所有小类class DataBase(Tools):    def __init__(self):        # self.tools = Tools()        self.url = 'xxxxx'           # 指标网站        self.headers = {'Connection': 'close'}            # 申请头,防止ssl报错        # self.big_num = 3                                  # 第几个大类,从0开始        # self.small_num = 0                                # 第几个小类,从0开始        self.net_xpath = '/html/body/div[5]/div/div[2]'   # 网站所有大类的table xpath        self.xpath_big = ['/html/body/div[3]/div[2]']     # 对应大类中的小类的table xpath        self.xpath_small = ['/html/body/div[4]/div']      # 对应小类的内容table xpath    def get_big_category(self):        """获取网站中所有的类别,寄存列表中"""        self.get_category(self.url,                          self.net_xpath,                          category_list,                          self.headers)        print(f"1========={category_list}")    def get_small_category(self, big_num):        """获取某个大类中小类所有的类别,寄存列表中"""        self.get_category(f'{self.url}{category_list[big_num]}',                          self.xpath_big[0],                          category_list_small,                          self.headers)        print(f"获取的大类是: {category_list[big_num]} ,如下:")    def get_small_content(self, i, small_num):        """获取小类中所有内容"""        print(f"获取的大类对应的小类是:{category_list_small[small_num]}")        time.sleep(1)        url_1 = f'{self.url}{category_list_small[small_num]}?page={i}'        print(f"申请的小类的域名为:{url_1}")        res = requests.get(url_1, verify=False, headers=self.headers)        tree = etree.HTML(res.content)        div = tree.xpath(self.xpath_small[0])        div_str = etree.tostring(div[0])        div_str1 = str(div_str, "UTF-8")        time.sleep(2)        return div_str1def main():    print("*" * 20)    print("在运行前请所相熟下规定:\n"          "1、依照网页显示,大类名称输出 数字-大类名称,如1-电阻器\n"          "2、小类名称输出 数字-小类名称,如1-固定电阻器\n"          "3、大小类前边的数字示意第几个\n"          "4、如果输错不做判断,只是寄存的门路须要本人查找,倡议一次性输出正确\n"          "5、!!!!!程序执行过程请勿敞开任何窗口!!!!!")    print("*" * 20)    base_file = os.path.dirname(os.path.abspath("test_database_final.py"))    print("#" * 20)    print("程序将开始执行,请稍后......\n"          "程序曾经启动~\n"          f"程序启动目录为:{base_file}\n"          "初始化数据......")    print("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n"          "1-电阻器        2-连接器          3-连接器支架      4-电容器                 5-振荡器              \n"          "6-晶体/谐振器    7-电源电路        8-开关           9-传感器/温度传感器        10-光电               \n"          "11-光纤         12-二极管         13-电路爱护      14-存储                  15-信号电路            \n"          "16-电感器       17-端子           18-插座          19-微控制器和处理器       20-射频和微波          \n"          "21-逻辑         22-晶体管         23-继电器        24-转换器                25-过滤器              \n"          "26-触发安装      27-RC网络        28-可编程逻辑     29-电信电路              30-驱动程序和接口       \n"          "31-放大器电路    32-耐热撑持安装    33-变压器        34-生产电路              35-电池               \n"          "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")    big = input("请输出大类的名称(如1-电阻器): ")    small = input("请输出小类的名称(如1-固定电阻器): ")    num = int(input("请输出小类的页数(如50,须要从网站查看): "))    b_n = int(input("请输出该大类对应的序号,共35个大类,从左到右数从0开始,比方0: "))    m_n = int(input("请输出该小类对应的序号,从0开始,比方0: "))    print(f"通过输出,咱们要获取的数据为:第{b_n + 1}个大类中的第{m_n + 1}个小类\n"          f"即:{big}中的{small}")    data_base = DataBase()    data_base.get_big_category()    data_base.get_small_category(b_n)    for i in range(1, num+1):        get_content = data_base.get_small_content(i, m_n)        print(f"第{i}次获取:获取的数据开始写入文件,文件名为:第{i}页.html")        file = f"{base_file}\\{big}\\{small}"        if os.path.exists(file) is False:            os.makedirs(file)        data_base.write_html(file=f"{file}\\第{i}页.html",                             txt=get_content)        time.sleep(1)    data_base.html_to_excel(base_file,                            big,                            small,                            f"{small}sheet.xlsx",                            f"{small}.xlsx")if __name__ == "__main__":    main()    input('Press Enter to exit…')

7 Pyinstaller打包

  • 间接应用以下命令打包:
pyinstaller -F  test_database_final.py
  • 打包后生成两个文件夹:

  • 双击运行如下exe即可:

8 运行成果