【问题标题】:BeautifulSoup: Parse JavaScript dynamic contentBeautifulSoup:解析 JavaScript 动态内容
【发布时间】:2019-05-24 06:49:47
【问题描述】:

我正在使用 BeautifulSoup 开发一个 python 网络 scraper,它解析来自 this 网站的“产品列表”并为每个产品列表提取一些信息(即价格、供应商等)。 我可以提取其中的许多信息,但其中一个(即产品数量)似乎是 raw html 中的 hidden 的。通过我的浏览器查看网页,我看到的是(unid = units):

product_name       1 unid      $10.00 

但它的 html 没有显示我可以提取的任何整数值。它显示了这个 html 文本:

<div class="e-col5 e-col5-offmktplace ">
  <div class="kWlJn zYaQqZ gQvJw">&nbsp;</div> 
  <div class="imgnum-unid"> unid</div>
</div>

我的问题是如何获取e-col5的这个隐藏内容,其中存储了产品数量?

import re
import requests
from bs4 import BeautifulSoup

page = requests.get("https://ligamagic.com.br/?view=cards%2Fsearch&card=Hapatra%2C+Vizier+of+Poisons")
soup = BeautifulSoup(page.content, 'html.parser')
vendor = soup.find_all('div', class_="estoque-linha", mp="2")
print(vendor[1].find(class_='e-col1').find('img')['title'])
print(vendor[1].find(class_='e-col2').find_all(class_='ed-simb')[1].string)
print(vendor[1].find(class_='e-col5'))

编辑: 隐藏内容在这种情况下代表 JavasSript 动态更新的内容。

【问题讨论】:

  • 看来隐藏的内容实际上是用 JavaScript 动态更新的。
  • 解析此类内容@LukaszSalitra 的正确方法是什么?
  • @delirium 一般情况下很难。在您的特定情况下,您可能希望查看 JavaScript 以了解它在做什么,并基本上在您的解析器中重新实现它。

标签: javascript python beautifulsoup


【解决方案1】:

@ewwink 找到了退出 unid 的方法,但无法退出价格。我试图在这个答案中提取价格。

目标 div sn-p:

<div mp="2" id="line_e3724364" class="estoque-linha primeiro"><div class="e-col1"><a href="b/?p=e3724364" target="_blank"><img title="Rayearth Games" src="//www.lmcorp.com.br/arquivos/up/ecom/comparador/155937.jpg"></a></div><div class="e-col9-mobile"><div class="e-mob-edicao"><img src="//www.lmcorp.com.br/arquivos/up/ed_mtg/AKH_R.gif" height="19"></div><div class="e-mob-edicao-lbl"><p>Amonkhet</p></div><div class="e-mob-preco e-mob-preco-desconto"><font color="gray" class="mob-preco-desconto"><s>R$ 1,00</s></font><br>R$ 0,85</div></div><div class="e-col2"><a href="./?view=cards/search&amp;card=ed=akh" class="ed-simb"><img src="//www.lmcorp.com.br/arquivos/up/ed_mtg/AKH_R.gif" height="21"></a><font class="nomeedicao"><a href="./?view=cards/search&amp;card=ed=akh" class="ed-simb">Amonkhet</a></font></div><div class="e-col3"><font color="gray" class="mob-preco-desconto"><s>R$ 1,00</s></font><br>R$ 0,85</div>
                            <div class="e-col4 e-col4-offmktplace">
                                <img src="https://www.lmcorp.com.br/arquivos/img/bandeiras/pten.gif" title="Português/Inglês"> <font class="azul" onclick="cardQualidade(3);">SP</font>

                            </div>
                        <div class="e-col5 e-col5-offmktplace "><div class="cIiVr lHfXpZ mZkHz">&nbsp;</div> <div class="imgnum-unid"> unid</div></div><div class="e-col8 e-col8-offmktplace "><div><a target="_blank" href="b/?p=e3724364" class="goto" title="Visitar Loja">Ir à loja</a></div></div></div>

如果我们仔细观察,我们可以,

for item in soup.findAll('div', {"id": re.compile('^line')}):
 print(re.findall("R\$ (.*?)</div>", str(item), re.DOTALL))

输出[截断]:

['10,00</s></font><br/>R$ 8,00', '10,00</s></font><br/>R$ 8,00']
['9,50</s></font><br/>R$ 8,55', '9,50</s></font><br/>R$ 8,55']
['9,50</s></font><br/>R$ 8,55', '9,50</s></font><br/>R$ 8,55']
['9,75</s></font><br/>R$ 8,78', '9,75</s></font><br/>R$ 8,78']
[]
[]

它提取主要块,我们会得到价格。 但这也会跳过多个项目。

要获取所有数据,我们可以使用 OCR API 和 Selenium 来完成此操作。我们可以使用以下 sn-p 捕获感兴趣的元素:

from selenium import webdriver
from PIL import Image
from io import BytesIO

fox = webdriver.Firefox()
fox.get('https://ligamagic.com.br/?view=cards%2Fsearch&card=Hapatra%2C+Vizier+of+Poisons')
#element = fox.find_element_by_id('line_e3724364')
element = fox.find_elements_by_tag_name('s')
location = element.location
size = element.size
png = fox.get_screenshot_as_png() # saves screenshot of entire page
fox.quit()

im = Image.open(BytesIO(png)) # uses PIL library to open image in memory

left = location['x']
top = location['y']
right = location['x'] + size['width']
bottom = location['y'] + size['height']


im = im.crop((left, top, right, bottom)) # defines crop points
im.save('screenshot.png') # saves new cropped image

得到了https://stackoverflow.com/a/15870708的帮助。

我们可以像上面那样使用re.findall() 进行迭代以保存所有图像。在我们拥有所有图像之后,我们可以使用 OCR Space 来提取文本数据。这是一个快速的 sn-p :

import requests


def ocr_space_file(filename, overlay=False, api_key='api_key', language='eng'):

    payload = {'isOverlayRequired': overlay,
               'apikey': api_key,
               'language': language,
               }
    with open(filename, 'rb') as f:
        r = requests.post('https://api.ocr.space/parse/image',
                          files={filename: f},
                          data=payload,
                          )
    return r.content.decode()

e = ocr_space_file(filename='1.png')

print(e) # prints JSON

1.png:

来自 ocr.space 的 JSON 响应:

{"ParsedResults":[{"TextOverlay":{"Lines":[],"HasOverlay":false,"Message":"Text overlay is not provided as it is not requested"},"TextOrientation":"0","FileParseExitCode":1,"ParsedText":"RS 0',85 \r\n","ErrorMessage":"","ErrorDetails":""}],"OCRExitCode":1,"IsErroredOnProcessing":false,"ProcessingTimeInMilliseconds":"1996","SearchablePDFURL":"Searchable PDF not generated as it was not requested."}

它给了我们,"ParsedText" : "RS 0',85 \r\n"

【讨论】:

  • 干得好!如何将图片与每个商家信息相关联?
【解决方案2】:

unid 保存在 JS 数组中

vetFiltro[0]=["e3724364",0,1,....];

1是unid,你可以用正则得到它

# e-col5
unitID = vendor[1].get('id').replace('line_', '') # line_e3724364 => e3724364
regEx = r'"%s",\d,(\d+)' % unitID
unit = re.search(regEx, page.text).group(1)
print(unit + ' unids')

【讨论】:

  • 感谢您的帮助!你是怎么发现的?我可以处理任何其他类似的 JavaScript 字段(例如价格)吗?
  • 很遗憾我找不到价格。
  • 还是谢谢你。你还能评论一下你是如何发现vetFiltro的吗?
  • 每个供应商都有像 line_e3724364 这样的 ID,删除了 line_ 我在页面源中找到了它。不客气。
【解决方案3】:

如果您仔细观察,unid 只是一个 div 中的图像,由class 移动到正确的数字。

例如unid1:

.jLsXy {
    background-image: url(arquivos/up/comp/imgunid/files/img/181224lSfWip8i1lmcj2a520836c8932ewcn.jpg);
}

是包含数字的图像。

.gBpKxZ {
background-position: -424px -23px;
}

是数字 1 的类

所以找到与数字匹配的 css 并创建你的表格(简单的方法)但不是最好的方法。

编辑: 似乎每次重新加载时都会更改位置(类),因此更难以将数字与图像匹配:(所以数字 1 可以从很多地方获取。

编辑2 我正在使用chrome devtools。 如果您检查unid,您还将找到每个班级的css。 所以在检查了网址之后就很清楚了。

【讨论】:

  • 感谢您的帮助 :) !您是如何发现数字是图像的?
  • @delirium 检查第二次编辑 :) 如果您需要更多解释,请问我 :)
猜你喜欢
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 1970-01-01
  • 2016-02-12
  • 1970-01-01
相关资源
最近更新 更多