【问题标题】:Grab the classname of nested HTML elements with Puppeteer使用 Puppeteer 获取嵌套 HTML 元素的类名
【发布时间】:2021-08-27 02:36:06
【问题描述】:

我正在尝试获取类名为 calendarCellRegularPast 的所有 td 元素我已尝试多次尝试使用 page.$page.$$page.eval 和许多其他元素,但似乎无法获得适当的元素。我得到了对象,但无法解析它。我知道有办法做到这一点,但老实说,我是 puppeteer 和 Javascript 的新手,只是想不通。

这是一个预定工作日的日历,我正在尝试进一步了解并获取日期和时间。我想在 for 循环中使用它,这样我就可以抓住所有的工作时间。

我不需要修复我的代码,因为我知道它很乱,但我只需要知道如何获取这些元素


到目前为止,这是我的一些代码,虽然我尝试了很多东西,但它很混乱

 const el = await page.$("#scrollContainer > table > tbody > tr:nth-child(2) > td:nth-child(1)")
 const className = await el.getProperty('className')

 const getElm = async() => {
    try {
        const elm = await page.evaluate(()=> {
            let persons = [];
            const weekElm = document.querySelectorAll("#scrollContainer > table > tbody");
            
            document.querySelector("#scrollContainer > table > tbody > tr:nth-child(2) > td:nth-child(3)")


            console.log('\n\n\n', weekElm.length, '\n\n\n');
            try {
                console.log(weekElm);
            } catch (e) {
                console.error('could not log "weeksElm"');
            }
            
            for (let i = 2; i < weekElm.length; i++) {
                try {
                    const trd = weekElm[i];
                    console.log(trd);
                    const tr = document.querySelectorAll(`tr:nth-child${i+1}`)
                    persons.push(tr);
                    try {
                        console.log('\n\n\n', tr, '\n\n\n');
                    } catch (e) {
                        console.error('could not log "\\n\\n\\n, tr, \\n\\n\\n"');
                    }
                    for ( let j = 0; j < tr.length; i++) {
                        const td = (tr.querySelectorAll(`td:nth-child${j}`))
                        try {
                            pass
                        } catch (e) {
                            console.error('could not log "weeksElm"');
                        }
                    }
                } catch (e) {}
            }
        })
        console.log(persons);
    } catch (e){
        console.error("unable to get the work schedule \n unfortunately i still work");
    }
}




}

【问题讨论】:

  • page.$$ 选择与选择器匹配的所有元素,因此您不需要使用 for 循环。从您的选择器的外观来看,我可以看到您对选择器没有很多经验。这是一个有用的网站:devhints.io/css。其余的孩子我看不到,给你推荐一个好的选择器。但应该看起来像这样:“tbody .calendarCellRegularPast”。该页面的链接会很有帮助。
  • @Invizi 大多数孩子都在一张照片中可见,我会链接该网站,但它需要登录,因为它是一个工作调度网站,所以它真的不可能,但我会看看那个网站,非常感谢!
  • @FiveFlyPaper 如果您的主要问题只是获取类的跨度,您可以尝试 page.$$("tbody span.calendarCellRegularPast") 。顺便说一句,具有日期和时间的元素是什么样的?
  • 一般来说,images of code 不是很有帮助——至少,为了能够执行代码和使用它而重新输入它是不切实际的,几乎总是需要编写一个体面的、准确的回答。您可以将 HTML 显示为字符串,可能使用相关的 JS 来重新创建实时页面行为并使用 page.setContent(html) 导航到它,或提供指向页面的实时链接。此外,除了元素之外,您最终想要获取哪些数据?感谢您的信息。
  • 另一个表中的一个表。看起来很有趣。试试“.etmCursor tr td”。然后您可以使用 .map 遍历它们,然后为您想要的每个单独项目使用更多选择器。如“.calendarDateNormal”和“.CalendarCellRegularPast:nth-child(1)”和“.CalendarCellRegularPast:nth-child(2)”。

标签: javascript html web-scraping puppeteer


【解决方案1】:

首先,我建议使用 http 客户端和 Cheerio.js 进行抓取,因为它更快、更轻量级。但我能理解你为什么会因为身份验证而使用 puppeteer。

您可以使用 puppeteer 通过 page.content() 获取页面的 html,然后将其传递给cheerio。但如果你想使用 http 客户端,它看起来像这样:

const axios = require("axios");
const cheerio = require("cheerio");

const PAGEURL = "example.com";

function getData() {
  return new Promise((resolve, reject) => {
    axios.get(PAGEURL).then(res => res.data).then(HTML => {
      const $ = cheerio.load(HTML);
      // scrape data here
      resolve()
    }).catch(err => reject(err));
  })
}

首先,这是您的 HTML 模型:

<table class="etmSchedualTable">
  <tbody>
    <tr>
      <td class="calanderCellRegularPast">
        <table class="etmCursor">
          <tbody>
            <tr>
              <td>
                <span class="calanderDateNormal"> 01</span>
              </td>
              <td>
                <span class="calanderCellRegularPast etmNoBorder">                
                  <span>17:15</span> " - "
                  <span>23:45</span>
                  <span class="etmMoreLink">more...</span>
                </span>
              </td>
            </tr>
            <tr>
              <td>
                <span class="calanderDateNormal"> 02</span>
              </td>
              <td>
                <span class="calanderCellRegularPast etmNoBorder">
                  <span>17:15</span> " - "
                  <span>23:45</span>
                  <span class="etmMoreLink">more...</span>
                </span>
              </td>
            </tr>
          </tbody>
        </table>
      </td>
      <tr>
  </tbody>
</table>

最后,我们想要一个包含如下对象的数组:

{
  date: 1,
  start: "17:45",
  end: "21:15"
}

为此,我们首先需要选择包含我们想要的所有项目的父级。在这种情况下,它是 $$(".etmCursor tbody tr")。这将使用表 .etmCursor 中所有 tr 的列表。

现在我们必须遍历表行 (tr) 并获取对象属性。

const timetable = $(".etmCursor tbody tr").map((i, elm) => {
  return {
    date: $(elm).find(".calendarDateNormal").text(),
    start: $(elm).find(".CalendarCellRegularPast:nth-child(1)").text(),
    end: $(elm).find(".CalendarCellRegularPast:nth-child(2)").text(),
  }
}).toArray();

.map() 方法与法线贴图相反,在法线贴图中您可以获取索引之前的元素。而且由于它使用了一个集合,所以我们必须使用 toArray() 将其转换为一个数组。

我们使用 .find() 对选定对象进行搜索。

.text() 然后使用该元素的文本。

现在我们应该有你想要的数据了。

【讨论】:

  • 所以我不断收到错误UnhandledPromiseRejectionWarning: TypeError: page.$(...).map is not a function。但是你在说这个cheerio的东西是什么?它会保存 html 的副本,然后只搜索吗?
  • 我可以使用page.content() 获取html,然后将其传递给cheerio?因为我的意思是这也可以工作
  • 我也不想打扰,但如果您在接下来的几天有空,也许可以聊天。我不能告诉你知道你在做什么,但我不能像我说的那样给你网站,因为它是基于登录的
  • 您将使用 page.content() 获取页面的 html 并将 HTML 替换为页面内容const $ = cheerio.load(await page.content())。如果您有更多问题,请尽管提问。
猜你喜欢
  • 2019-07-06
  • 2016-04-25
  • 1970-01-01
  • 2021-05-16
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多