【问题标题】:extract text from strong tag从强标签中提取文本
【发布时间】:2020-03-08 18:35:27
【问题描述】:

我正在尝试分别从cars.com 中提取外观颜色、内饰颜色、传输信息。

HTML:

<ul class="listing-row__meta">
 <li>
  <strong>
    Ext. Color:
  </strong>
    Gray
 </li>
 <li>
  <strong>
    Int. Color:
  </strong>
    White
 </li>
 <li>
  <strong>
    Transmission:
  </strong>
    Automatic
 </li>

我尝试了以下代码,但它显示“预期的字符串或类似字节的对象”。任何建议或解决方案将不胜感激。

from bs4 import BeautifulSoup
import urllib
import re

url ='https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
response = requests.get(url)
page = response.text
soup = BeautifulSoup(page, 'lxml')

all_matches = soup.find_all('div',{'class':'shop-srp-listings__listing-container'})

for each in all_matches:

    info=each.findAll('ul',class_='listing-row__meta')
    pattern=re.compile(r'Ext. Color:')
    matches=pattern.finditer(info)
    for match in matches:
        print(match.text)

【问题讨论】:

标签: python regex web-scraping beautifulsoup


【解决方案1】:

正则表达式库的findAll function returns a List of results;所以info 是一个字符串数组,而不是单个字符串。您可能还需要遍历 info 中的每个项目。

这些对象返回bs4.Tag 对象(不是字符串),可以将其转换为字符串,以便它们适合finditer API。 (这特别令人困惑,因为当您打印对象 info! 时,bs4 会将它们呈现为字符串一样!)

for each in all_matches:
    info = each.findAll('ul', class_='listing-row__meta')
    for item in info:
        pattern = re.compile(r'Ext. Color:')
        matches = pattern.finditer(str(item))
        for match in matches:
            print(match.text)

在本例中,info 可能是长度 = 1 的列表;在这种情况下,如果您确定只需要第一个结果,并且只有一个结果,您可以转换为返回单个事件的调用,或者简单地将第一个结果与此行一起使用:

info = each.findAll('ul', class_='listing-row__meta')[0]

然后按原样使用问题中的代码。

【讨论】:

  • 您提供的代码仍然显示“预期的字符串或类似字节的对象”
  • 对不起!发布我的解决方案的第二个最新版本。更新了!
【解决方案2】:

你得到的错误可以通过类型转换为 str 来修复:

    matches=pattern.finditer(info)

改为:

    matches=pattern.finditer(str(info))

【讨论】:

    【解决方案3】:

    也许,这会更接近您可能尝试提取的内容,我猜,其表达式类似于:

    (?is)<strong>\s*([^<]*?)\s*<\/strong>
    

    或者,

    (?is)(?<=<strong>)\s*[^<]*?\s*(?=<\/strong>)
    

    很确定,您也可以使用 bs4 内置函数来做到这一点。

    测试 1

    from bs4 import BeautifulSoup
    import urllib
    import re
    import requests
    
    url = 'https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
    response = requests.get(url)
    page = response.text
    soup = BeautifulSoup(page, 'lxml')
    
    all_matches = soup.find_all(
        'div', {'class': 'shop-srp-listings__listing-container'})
    
    for each in all_matches:
        info = each.findAll('ul', class_='listing-row__meta')
        matches = re.findall(
            r'(?is)<strong>\s*[^<]*?\s*<\/strong>\s*([^<]*?)\s*<', str(info[0]))
        for match in matches:
            print(match)
    

    输出 1

    Gray
    Beige
    Automatic
    AWD
    Gray
    White
    Automatic
    AWD
    Black
    

    测试 2

    如果你愿意,你也可以通过一些修改来制作一个 dict:

    from bs4 import BeautifulSoup
    import urllib
    import re
    import requests
    
    url = 'https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
    response = requests.get(url)
    page = response.text
    soup = BeautifulSoup(page, 'lxml')
    
    all_matches = soup.find_all(
        'div', {'class': 'shop-srp-listings__listing-container'})
    
    for each in all_matches:
        info = each.findAll('ul', class_='listing-row__meta')
        matches = dict(re.findall(
            r'(?is)<strong>\s*([^<]*?)\s*<\/strong>\s*([^<]*?)\s*<', str(info[0])))
    
        for k, v in matches.items():
            print(f'{k} {v}')
    

    输出 2

    Ext. Color: Gray
    Int. Color: Beige
    Transmission: Automatic
    Drivetrain: AWD
    Ext. Color: Gray
    Int. Color: White
    Transmission: Automatic
    Drivetrain: AWD
    Ext. Color: Black
    

    测试 3

    如果您想列出:

    from bs4 import BeautifulSoup
    import urllib
    import re
    import requests
    
    url = 'https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
    response = requests.get(url)
    page = response.text
    soup = BeautifulSoup(page, 'lxml')
    
    all_matches = soup.find_all(
        'div', {'class': 'shop-srp-listings__listing-container'})
    
    for each in all_matches:
        info = each.findAll('ul', class_='listing-row__meta')
        matches = re.findall(
            r'(?is)<strong>\s*([^<]*?)\s*<\/strong>\s*([^<]*?)\s*<', str(info[0]))
    
        for match in matches:
            print(list(match))
    

    输出

    ['Transmission:', 'Automatic']
    ['Drivetrain:', 'RWD']
    ['Ext. Color:', 'Gray']
    ['Int. Color:', 'Gray']
    ['Transmission:', 'Automatic']
    ['Drivetrain:', 'RWD']
    ['Ext. Color:', 'White']
    ['Int. Color:', 'Black']
    ['Transmission:', 'Automatic']
    ['Drivetrain:', 'RWD']
    ['Ext. Color:', 'White']
    ['Int. Color:', 'Beige']
    ['Transmission:', 'Automatic']
    ['Drivetrain:', 'AWD']
    ['Ext. Color:', 'Gray']
    ['Int. Color:', 'Beige']
    ['Transmission:', 'Automatic']
    ['Drivetrain:', 'AWD']
    ['Ext. Color:', 'White']
    

    测试 4

    from bs4 import BeautifulSoup
    import urllib
    import re
    import requests
    
    url = 'https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
    response = requests.get(url)
    page = response.text
    soup = BeautifulSoup(page, 'lxml')
    
    all_matches = soup.find_all(
        'div', {'class': 'shop-srp-listings__listing-container'})
    
    
    keys = ['Ext. Color', 'Int. Color', 'Transmission', 'Drivetrain']
    
    outputs = dict()
    
    for each in all_matches:
        info = each.findAll('ul', class_='listing-row__meta')
        matches = dict(re.findall(
            r'(?is)<strong>\s*([^<:]*?)\s*:\s*<\/strong>\s*([^<]*?)\s*<', str(info[0])))
    
        for item in matches.items():
            if item[0] not in outputs:
                outputs[item[0]] = [item[1]]
            if item[0] in keys:
                outputs[item[0]].append(item[1])
    

    输出 4

    {'分机。颜色:['银色','银色','白色','白色','黑色','灰色', '灰色','黑色','黑色','白色','蓝色','红色','银色','灰色', '黑色','白色','黑色','灰色','白色','黑色','黑色'],'诠释。 颜色:['米色','米色','黑色','白色','黑色','黑色','灰色', '米色','黑色','黑色','米色','米色','黑色','黑色', '黑色','黑色','黑色','黑色','白色','白色','黑色'], '传输':['自动','自动','自动','自动', “自动”、“自动”、“自动”、“自动”、“自动”、 “自动”、“自动”、“自动”、“自动”、“自动”、 “自动”、“自动”、“自动”、“自动”、“自动”、 '自动','自动'],'传动系统':['AWD','AWD','AWD','AWD', 'RWD','RWD','RWD','RWD','AWD','RWD','RWD','RWD','AWD','RWD', 'RWD'、'AWD'、'RWD'、'AWD'、'AWD'、'AWD'、'AWD']}

    测试 5

    from bs4 import BeautifulSoup
    import urllib
    import re
    import requests
    
    url = 'https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX'
    response = requests.get(url)
    page = response.text
    soup = BeautifulSoup(page, 'lxml')
    
    all_matches = soup.find_all(
        'div', {'class': 'shop-srp-listings__listing-container'})
    
    
    keys = ['Ext. Color', 'Int. Color', 'Transmission', 'Drivetrain']
    
    outputs = dict()
    
    for each in all_matches:
        info = each.findAll('ul', class_='listing-row__meta')
        matches = dict(re.findall(
            r'(?is)<strong>\s*([^<:]*?)\s*:\s*<\/strong>\s*([^<]*?)\s*<', str(info[0])))
    
        for item in matches.items():
            if item[0] not in outputs:
                outputs[item[0]] = [item[1]]
            if item[0] in keys:
                outputs[item[0]].append(item[1])
    
    
    print(outputs)
    
    print('*' * 50)
    
    no_duplicate_outputs = dict()
    for item in outputs.items():
        if item[0] not in no_duplicate_outputs:
            no_duplicate_outputs[item[0]] = list(set(item[1]))
    
    print(no_duplicate_outputs)
    

    输出 5

    {'分机。颜色:['黑色','黑色','白色','黑色','其他','灰色', '白色','白色','灰色','白色','灰色','银色','蓝色','黑色', '银色','银色','黑色','蓝色','蓝色','黑色','白色'],'诠释。 颜色:['黑色','黑色','米色','米色','黑色','灰色','黑色', '米色','米色','白色','黑色','黑色','灰色','黑色','黑色', '灰色','黑色','黑色','黑色','白色','黑色'],'传输': ['自动','自动','自动','自动','自动', “自动”、“自动”、“自动”、“自动”、“自动”、 “自动”、“自动”、“自动”、“自动”、“自动”、 “自动”、“自动”、“自动”、“自动”、“自动”、 '自动'], '传动系统': ['AWD', 'AWD', 'RWD', 'RWD', 'RWD', 'RWD', 'RWD','AWD','AWD','AWD','RWD','AWD','AWD','AWD','AWD','AWD', 'RWD'、'AWD'、'AWD'、'AWD'、'AWD']} ****************************************************** {'分机。颜色:['银色','白色','蓝色','其他','黑色','灰色'],'诠释。颜色': ['米色','白色','黑色','灰色'],'传输':['自动'], '传动系统':['RWD','AWD']}


    如果您希望简化/修改/探索表达式,在regex101.com 的右上角面板中已对此进行了说明。如果您愿意,您还可以在this link 中观看它如何与一些示例输入相匹配。


    正则表达式电路

    jex.im 可视化正则表达式:

    【讨论】:

    • 分号后面的文字是否也可以获取(ex.Ext.Color: Gray)
    • 我想通过创建exterior_color=[ ]、interior color=[ ]、transmission=[ ]、drivetrain=[ ],将外部颜色、内部颜色、变速器、传动系统分别放在一个列表中。关于如何从比赛中提取这些信息的任何建议?谢谢
    • 是的,这就是我想要得到的。谢谢你。但是每个项目应该只有 20 条记录。重复每个项目的第一条记录。例如,它应该像 {'Ext.颜色':['Silver', 'White', 'White', 'Black', .....]} 而不是两个银色{'Ext.颜色:['银色','银色','白色','白色','黑色',.....]}。这怎么可能解决
    • 我的意思是 ext.color 列表中应该只有 20 个值,因为我们正在抓取的网站中只有 20 辆汽车。尽管如此,您在上面的输出 4 中显示的 ext.color 的输出中有 21 个值。
    【解决方案4】:

    这里绝对不需要正则表达式。 html 是常规的,使用 bs4 4.7.1 + 您可以使用 :contains 通过文本定位适当的元素,然后使用 next_sibling 获取包含值的相邻节点。抓取列表,压缩并转换为数据框

    import pandas as pd
    import requests
    from bs4 import BeautifulSoup as bs
    
    headers = ['Make','Ext','Int','Trans','Drive']
    r = requests.get('https://www.cars.com/for-sale/searchresults.action/?zc=92617&rd=10&stkTypId=28881&mkId=28263&searchSource=RESEARCH_SHOP_INDEX')
    soup = bs(r.content, 'lxml')
    make = [i.text.strip() for i in soup.select('.listing-row__title')]
    ext_color = [i.next_sibling.strip() for i in soup.select('strong:contains("Ext. Color:")')]
    int_color = [i.next_sibling.strip() for i in soup.select('strong:contains("Int. Color:")')]
    transmission = [i.next_sibling.strip() for i in soup.select('strong:contains("Transmission:")')]
    drive = [i.next_sibling.strip() for i in soup.select('strong:contains("Drivetrain:")')]
    df = pd.DataFrame(zip(make, ext_color, int_color, transmission, drive), columns = headers)
    print(df)
    

    【讨论】:

      猜你喜欢
      • 2010-12-09
      • 2021-05-05
      • 1970-01-01
      • 1970-01-01
      • 2021-10-22
      • 2012-05-05
      • 2023-04-02
      • 1970-01-01
      相关资源
      最近更新 更多