【问题标题】:Parsing data according html table根据html表解析数据
【发布时间】:2021-12-29 20:50:45
【问题描述】:

我目前在合并我用 beautifulsoup 提取的数据方面遇到了困难,很遗憾我不知道如何解决这个问题。

实际上,我正在寻找包含在表格中的每个条码作为 html,详细的产品。知道在我解析的每一页上我可以有多个条形码。

代码下方:

import requests
from bs4 import BeautifulSoup


###################_____Parameter_____###################
url = 'https://rappel.conso.gouv.fr'
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'}
#########################################################

#Collecting links on rappel.gouv
def get_soup(url):
    r = requests.get(url, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')
    return soup

def extract_product_urls(url):
    links = [url+x['href'] for x in get_soup(url).select('a.product-link')]
    return links

soup = get_soup(url)
url_data = extract_product_urls(url)

#Collecting data on each url collected
def data(url_data):
    data_set = []
    #Collecting bar_code
    for row in url_data:
        req_bar = requests.get(row, headers=headers)
        ext_bar = BeautifulSoup(req_bar.text, 'html.parser')
        for code_bar in ext_bar.find_all('tbody', {'class' : 'text-left'}):
            table_content_tr = code_bar.findAll('tr')
            for td in table_content_tr:
                all_data = td.findAll('td')
                all_data = [x.text.strip() for x in all_data]

    #collecting detailled products            
    for data in url_data:
        req = requests.get(data, headers=headers)
        ext = BeautifulSoup(req.text, 'html.parser')
        for products in ext.find_all('div', {'class' : 'row site-wrapper'}):
            title = products.find('p', {'class' : 'h5 product-main-title'}).text
            brand = products.find('p', {'class' : 'text-muted product-main-brand'}).text.replace('\xa0:\n',': ').strip()
            category = products.find('p', {'class' : 'product-cat'}).text

            detail_rappel = products.find_all('div', {'class' : 'card product-practical'})
            for motif in detail_rappel:
                val_1 = motif.find('span', {'class': 'val'}).text    

        results = *all_data, title, brand, category, val_1
        data_set.append(results)
    return data_set


#final result
final = data(url_data)
for i in final:
    print(i)

这是我得到的实际结果:

('8712364296389', 'Tous les lots', 'BOUQUET GARNI POT AU FEU BIOLOGIQUE', 'Marque: LES HERBES DE MON PERE', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('8712364296389', 'Tous les lots', 'BOUILLOTTE DE NOEL', 'Marque: NOCIBE', 'Divers', 'Risque de bouillotte défectueuse pouvant entraîner risque de sécurité du consommateur lors du réchauffage des bouillottes par micro-ondes (brûlures et 
incendie) ')
('8712364296389', 'Tous les lots', 'BOTTE THYM', 'Marque: Sans marque', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('8712364296389', 'Tous les lots', 'BOTTE THYM BIOLOGIQUE', 'Marque: Sans marque', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('8712364296389', 'Tous les lots', 'Rôti de porc farci', 'Marque: Sans Marque', 'Viandes', 'Détection de Salmonelle')
('8712364296389', 'Tous les lots', 'Grille Pain TL640810', 'Marque: Tefal', 'Appareils électriques, électroménager', 'Risque de choc électrique ')
('8712364296389', 'Tous les lots', 'Coppa vendue en tranche au rayon Coupe', 'Marque: Sans marque', 'Autres', 'Présence de Listeria monocytogenes <10 ufc/g')
('8712364296389', 'Tous les lots', 'JOUETS ARC 25 CM ET 2 FLECHES AVEC VENTOUSE', 'Marque: B&G INTERNATIONAL  - BGI', 'Jouets', 'rappel  pour une non-conformité sur la solidité de la fixation de la ventouse de la flèche qui ne tient pas 
et se détache de la tige')
('8712364296389', 'Tous les lots', 'DEGUISEMENT PETITE SORCIERE PINK WITCH', 'Marque: FIESTAS GUIRCA', 'Articles pour enfants et puériculture', "rappel pour une non-conformité sur la fragilité des coutures des sequins  risquant de casser lors d'une trop forte traction")
('8712364296389', 'Tous les lots', 'DEGUISEMENT BEBE LICORNE', 'Marque: FUNNY FASHION', 'Articles pour enfants et puériculture', "rappel concernant le produit pour une non-conformité sur la tirette de la fermeture éclair qui risque de casser lors d'une trop forte pression")

这是我真正想要的:[期望结果]

('3333313501047', 'F993002', 'Date de durabilité minimale 31/12/2024', 'BOUQUET GARNI POT AU FEU BIOLOGIQUE', 'Marque: LES HERBES DE MON PERE', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('3466762557225', 'M211', '', 'BOUILLOTTE DE NOEL', 'Marque: NOCIBE', 'Divers', 'Risque de bouillotte défectueuse pouvant entraîner risque de sécurité du consommateur lors du réchauffage des bouillottes par micro-ondes (brûlures et incendie) ')
('3466762557232', 'M211', '', 'BOUILLOTTE DE NOEL', 'Marque: NOCIBE', 'Divers', 'Risque de bouillotte défectueuse pouvant entraîner risque de sécurité du consommateur lors du réchauffage des bouillottes par micro-ondes (brûlures et incendie) ')
('3333313131183', 'F21101308407L03', '', 'BOTTE THYM', 'Marque: Sans marque', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('3333313131183', 'F21101308407L04', '', 'BOTTE THYM', 'Marque: Sans marque', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('3333313131183', 'F21101308407L03', '', 'BOTTE THYM BIOLOGIQUE', 'Marque: Sans marque', 'Fruits et légumes', "présence d'un résidu de pesticide interdit")
('', '21355-8810', 'Date limite de consommation 29/12/2021', 'Rôti de porc farci', 'Marque: Sans Marque', 'Viandes', 'Détection de Salmonelle')
('', 'Tous les lots', '', 'Grille Pain TL640810', 'Marque: Tefal', 'Appareils électriques, électroménager', 'Risque de choc électrique ')
('0208465000000', 'Coppa vendue au rayon charcuterie coupe du 09/11/21 au 28/11/21', '', 'Coppa vendue en tranche au rayon Coupe', 'Marque: Sans marque', 'Autres', 'Présence de Listeria monocytogenes <10 ufc/g')
('3588270030394', 'Tous les lots', '', 'JOUETS ARC 25 CM ET 2 FLECHES AVEC VENTOUSE', 'Marque: B&G INTERNATIONAL  - BGI', 'Jouets', 'rappel  pour une non-conformité sur la solidité de la fixation de la ventouse de la flèche qui ne tient pas et se détache de la tige')
('8434077830369', 'Tous les lots', '', 'DEGUISEMENT PETITE SORCIERE PINK WITCH', 'Marque: FIESTAS GUIRCA', 'Articles pour enfants et puériculture', "rappel pour une non-conformité sur la fragilité des coutures des sequins  risquant de casser lors d'une trop forte traction")
('8434077830376', 'Tous les lots', '', 'DEGUISEMENT PETITE SORCIERE PINK WITCH', 'Marque: FIESTAS GUIRCA', 'Articles pour enfants et puériculture', "rappel pour une non-conformité sur la fragilité des coutures des sequins  risquant de casser lors d'une trop forte traction")
('8712364296396', 'Tous les lots', '', 'DEGUISEMENT BEBE LICORNE', 'Marque: FUNNY FASHION', 'Articles pour enfants et puériculture', "rappel concernant le produit pour une non-conformité sur la tirette de la fermeture éclair qui risque de casser lors d'une trop forte pression")
('8712364296372', 'Tous les lots', '', 'DEGUISEMENT BEBE LICORNE', 'Marque: FUNNY FASHION', 'Articles pour enfants et puériculture', "rappel concernant le produit pour une non-conformité sur la tirette de la fermeture éclair qui risque de casser lors d'une trop forte pression")
('8712364296389', 'Tous les lots', '', 'DEGUISEMENT BEBE LICORNE', 'Marque: FUNNY FASHION', 'Articles pour enfants et puériculture', "rappel concernant le produit pour une non-conformité sur la tirette de la fermeture éclair qui risque de casser lors d'une trop forte pression")

还有以下问题: 每个页面可能有不同的表格框架:以下 3 个示例 示例 1

  <table class="table w-auto">
        <thead class="thead-light">
                <th>GTIN</th>
                <th>Lot</th>
            </thead>
            <tbody class="text-left">
                    <tr>
                            <td colspan="1">
                                8712364296396
                            </td>
                            <td colspan="2">
                                Tous les lots
                            </td>
                    </tr>
                    <tr>
                            <td colspan="1">
                                8712364296372
                            </td>
                            <td colspan="2">
                                Tous les lots
                            </td>
                    </tr>
                    <tr>
                            <td colspan="1">
                                8712364296389
                            </td>
                            <td colspan="2">
                                Tous les lots
                            </td>
                    </tr>
            </tbody>
        </table>

示例 2:

    <table class="table w-auto">
        <thead class="thead-light">
                <th>Lot</th>
            </thead>
            <tbody class="text-left">
                    <tr>
                            <td colspan="3">
                                Tous les lots
                            </td>
                    </tr>
            </tbody>
        </table>

示例 3:

    <table class="table w-auto">
        <thead class="thead-light">
                <th>GTIN</th>
                <th>Lot</th>
                <th>Date</th>
            </thead>
            <tbody class="text-left">
                    <tr>
                            <td colspan="1">
                                3333313501047
                            </td>
                            <td colspan="1">
                                F993002
                            </td>
                            <td colspan="1">
                                Date de durabilit&#xE9; minimale 31/12/2024
                            </td>
                    </tr>
            </tbody>
        </table>

主要问题是html中的表格,我想考虑所有数据至少有“GTIN”,“Lot”和“Date”,无论数据是否存在。如果它不存在那么你写一个空格''(就像我在上面的期望结果中写的那样)

最后,所有数据都将被放入 mysql 数据库中,所以我不知道是否与创建已知列相关(使用 pandas 或制表)我将使用 mysql 连接器创建它们。

对不起,如果有点混乱,英语不是我的母语 :) 如果您有任何问题,我很乐意回答 ^^

【问题讨论】:

    标签: python html python-3.x beautifulsoup


    【解决方案1】:

    这并不完美,但我认为它会为您提供所需的内容。您的第一个循环遍历数据以收集 GTIN、LOT 和 Date 正在覆盖自身。在 cmets 中查找“添加”和“删除”。我还有一种查看注释掉的结果的方法。 (如果您想使用该代码,它就可以工作。)我还有两个没有被注释掉。最后一个版本需要打包的tabulate。此代码还需要包numpyre

    我包含了您的所有原始代码和更改。如果有什么我没有澄清的地方,请告诉我。

    import requests
    from bs4 import BeautifulSoup
    import numpy as np
    import re
    import tabulate
    
    
    ###################_____Parameter_____###################
    url = 'https://rappel.conso.gouv.fr'
    headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'}
    #########################################################
    
    #Collecting links on rappel.gouv
    def get_soup(url):
        r = requests.get(url, headers=headers)
        soup = BeautifulSoup(r.text, 'html.parser')
        return soup
    
    def extract_product_urls(url):
        links = [url+x['href'] for x in get_soup(url).select('a.product-link')]
        return links
    
    soup = get_soup(url)
    url_data = extract_product_urls(url)
    
    #Collecting data on each url collected
    def data(url_data):
        data_set = []
        all_data = []        # <-- ADDED
        tbler = []           # <-- ADDED
    
        #Collecting bar_code
        for row in url_data:
            req_bar = requests.get(row, headers=headers)
            ext_bar = BeautifulSoup(req_bar.text, 'html.parser')
            #for code_bar in ext_bar.find_all('tbody', {'class' : 'text-left'}):  <-- REMOVED
            for code_bar in ext_bar.find_all('table'):
                table_content_tr = code_bar.findAll('tr')
                for td in table_content_tr:
                    all_data = td.findAll('td')
                    # all_dt2 = [x.text.strip(' ') for x in all_data]          <-- REMOVED
                    all_dt1 = [x.get_text(strip = True) for x in all_data]    # <-- ADDED
    
            # all_data was being overwritten before
            all_dt1 = np.ravel(all_dt1) # one dimensional array        # <-- ADDED
    
            # check for GTIN, Lot, and date                             <--- Added here down
            if len(all_dt1) < 3:
                # check for product GTIN
                if not re.match('^(\d{13})$', all_dt1[0, ]):
                     # add a blank (underscore) for missing GTIN
                    all_dt1 = np.hstack(('_', all_dt1))    
                # check the lot field for dates (missing lot)
                if re.search('[\d]{2}/[\d]{2}/[\d]{2,4}', all_dt1[1, ]):
                    all_dt1 = np.insert(all_dt1, 1, "_")
                # missing date? any remaining that are missing collected here
                s = 3 - len(all_dt1)
                for i in range(0, s):
                    all_dt1 = np.hstack((all_dt1, ['_'])) # append for any other missing fields
            # stack new and existing product data
            if len(tbler) > 0:                           # is this the first time through the for loop?
                tbler = np.vstack((tbler, all_dt1))      # stack existing rows
            else: tbler = all_dt1                        #  or else, create first row
                                                                     # ----- end added here
        # removed ... don't loop twice
    
        #collecting detailled products
        #for data in url_data:                                           <-- REMOVED
        #    req = requests.get(data, headers=headers)                   <-- REMOVED
        #    ext = BeautifulSoup(req.text, 'html.parser')                <-- REMOVED
            for products in ext_bar.find_all('div', {'class' : 'row site-wrapper'}): # mod!! ext => ext_bar
                title = products.find('p', {'class' : 'h5 product-main-title'}).text
                brand = products.find('p', {'class' : 'text-muted product-main-brand'}).text.replace('\xa0:\n',': ').strip()
                category = products.find('p', {'class' : 'product-cat'}).text
                detail_rappel = products.find_all('div', {'class' : 'card product-practical'})
                for motif in detail_rappel:
                    val_1 = motif.find('span', {'class': 'val'}).text
            results = row, title, brand, category, val_1                             # mod!! data => row, removed *all_data
            data_set.append(results)
    
        return data_set, tbler #, all_data   <--- MOD, => added tbler
    
    
    # final result
    final, eb = data(url_data)
    
    # column stack the GTIN, lot, and date with the product data
    newArr = np.hstack((eb, final))
    
    # this commented out for loop will print by array row
    # x, y = np.shape(newArr)
    # for i in range(0, x):
    #    print(newArr[i, ])
    
    # this is just an alternative print method (it doesn't how the brackets or quotes)
    for i in newArr:
        for j in i:
            print(j, end = ' ')
        print()
    
    # yet another alternate look at the same inforamtion
    print(tabulate.tabulate(newArr))
    

    第一个编码输出快照

    tabulate的使用(因为太宽了,拍两张)

    【讨论】:

    • 非常感谢 Kat,我远远超出了我的想象。我不知道我们可以为此使用 Numpy。我有几个问题:为什么当我打印时:all_dt1 = np.ravel(all_dt1) 我只得到一个结果? re.matchre.search 有什么区别?这种情况不一样吗?在这种情况下:np.hstacknp.insert 之间有什么区别?我想我会花费数小时或数天来了解该代码的每个过程:) 这对我来说很新^^
    • 好的,所以我不会说这完全正确,但据我了解... re.match 正在寻找要匹配的全部内容。 re.search 在字符串中搜索。 np.hstack 将两个东西水平堆叠在一起——比如列(相对于行)。 np.insert 可用于将某些东西放在特定位置。因此,如果我有一个 100 x 100 的数组或矩阵,我可以使用 np.insert 更改第 31 列第 49 行的值。
    • 我忘了解决 np.ravel! np.ravel 确保它是一维数组。当您打印 np.ravel 输出时,您会看到一个 1 x 3 数组(除非这三个数组中缺少某些东西)。
    • 感谢 Kat 的回答,我忘记了:为什么要调用两个参数 final, eb = data(url_data) ?我尝试在没有eb 的情况下打印,我看到了结果,除非包含 GTIN、Lot 和 Date 的表格出现在最后一列中,否则我看不到任何变化。最后一个问题,你知道我在哪里可以找到 Regex 的好教程吗? ^^ 我迷失了你写的所有内容:D
    • return 有两个变量,所以receipt 有两个变量:return data_set, tblerfinal, eb = data(url_data)。方法 (def) 中 data_set 中的内容在 final 中。 tbler 中的内容在 eb 中。是的,GTIN、日志和日期在eb 中。至于 Python 的正则表达式,this site should be able to help with that.
    猜你喜欢
    • 2017-03-03
    • 2011-11-24
    • 2011-03-26
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多