Missing dead_page condition.

使用 navigate 指令时,应添加 dead_page 条件以检查是否未找到该页面。这将防止自动重试。 虽然当响应状态为 404 时,我们会自动处理此问题,但在某些情况下,网站可能会使用其他各种状态代码进行响应:

Here are good and bad practices examples (you can navigate between them by clicking on the “不好” “Good” tabs)

try {
  // no need to wait 30sec 'ok-selector' in case of dead_page()
  wait('ok-selector');
} catch(e) {
  // in this case we can't be sure that the page is real dead
  dead_page('Page doesn\'t exist');
}

尽量减少对浏览器的请求量

一些交互指令,例如点击类型 el_exists el_is_visible wait wait_visible,会向浏览器发出真实请求,因而可能会增加延迟并降低性能。 建议组合选择器,进行单个调用,而非多个调用。

if (!(el_exists('#price1')) || el_exists('#price2')
  || el_exists('#price3') || el_exists('#discount'))
{
    dead_page('No price found');
}

Incorrect usage of rerun_stage()

当网站在页面上进行分页并且需要所有页面的数据时,则应从根页面为每个页面调用 rerun_stage(),而不是从每个页面调用。这使系统能够并行处理请求并提高抓取工具的速度。

navigate(input.url);
let $ = html_load(html());
let next_page_url = $('.next_page').attr('href');
rerun_stage({url: next_page_url});

Use close_popup() to close popups

不要花时间等待弹出窗口的出现。 使用 close_popup('popup_selector', 'close_button_selector') 关闭弹出窗口。弹出窗口可以随时出现,在大多数情况下,在每个交互指令之前添加验证检查是不可取的。 不好

navigate('https://example.com');
try {
  wait_visible('.cky-btn-accept', {timeout: 5000});
  click('.cky-btn-accept');
} catch(e) {
    console.log('Accept cookies button does not exist, continue');
}

Use wait_for_parser_value() with tag_response()

在使用 tag_response 指令并需要确保在从页面采集数据之前完成请求时,应使用 wait_for_parser_value()

tag_response('product', /api\/product/);
navigate('https://example.com');

// parser code
// in this case we can't be sure that the request is finished
let {product} = parser;
return product.data;

自定义错误消息

尽可能避免使用自定义错误消息。 我们的系统尽最大努力为您提供最准确的错误消息:

try {
  wait('selector1');
  //some code
  wait('selector2');
  //some code
} catch(e) {
  throw "Page not loaded properly"
}

网站响应缓慢,超时增加

如果网站无法正常加载,则可能是由于对等连接不良所致。 建议显示错误消息,系统将尝试使用更稳定的对等会话加载页面。

// 120 sec to long for waiting
wait('selector', {timeout: 120000});

重试机制

抓取工具代码应清晰,仅专注于抓取数据的必要任务。 没有必要白费力气做重复工作。最好强调与代码无关的问题并在系统中报告这些问题。

let counter = input.counter || 5;
while (counter > 1) {
  try {
    wait('selector' , {timeout: 500});
    click('selector');
    type('selector');
    //some code
    break;
  } catch(e) {
    // not acceptable use rerun_stage to create new session in case of error
    return rerun_stage({...input, counter: --counter});
  }
}


避免使用 try-catch 块

这有助于开发简洁易读的代码,无需依赖 try-catch 块即可有效地管理潜在的 “空”值 或“未定义”值。

try {
  const example = obj.prop;
} catch(e) {}

解析器代码:从元素集中获取值

最佳实践代码采用更简洁、更实用的 toArray()map() 方法,而非传统的 each() 循环。 这可增强代码的可读性并保持声明样式。

const links = [];
$('.card.product-wrapper').each(function(i, el) {
  links.push({url: $(this).find('h4 a').attr('href')});
})
return links;

规范化文本

我们在 cheerio prototype $(selector).text_sane() 中添加了一个自定义函数,它删除了所有不必要的空白字符并将其替换为单个空格。

$.prototype.clearText = function () {
  return this.text().replace(/\s+/g, ' ').trim();
}