【问题标题】:Curl how to scrape over 500 urls - the safe and resource-wise way卷曲如何抓取 500 多个网址 - 安全且资源明智的方式
【发布时间】:2019-01-28 00:09:13
【问题描述】:

我有一个包含 500 多个 URL 的列表,我必须抓取,因为我的分销商不提供 api 或 csv。该列表实际上是一个数组,其中包含我要跟踪的那些产品的 id:

$arr = [1,2,3,...,564];

url是一样的,你只改变它末尾的id:

$url = 'https://shop.com/products.php?id='

现在,在localhost 上,我使用foreach loop 抓取每个网址:

foreach($arr as $id){
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url . $id);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
   
    //preg_meth_all - get the data that i'm looking for
    //put that data into an array

    curl_close($ch);

}

但问题在于,首先,我认为这根本不明智 - 我知道这是一个事实,因为当我(意外)运行脚本(在本地主机上)时,我的访问被禁止/阻止了shop.com - 收到消息:Too many requests...429:D。

我试图 sleep 使用 10 作为 modulus 的每个 10 循环进行 foreach

$x = 0;
foreach($arr as $id){
   
    //ch request - get data and add it into an array 
    $x++;
    if($x % 10 == 0){
        sleep(2);
    }      

}

但这需要永远执行。

即使我能够连接并从每个单独的产品中获取我需要的日期,我也想使用curl(因为没有 api 或 csv)找到一个解决方案,该解决方案将立即运行该脚本,但在安全的情况下/明智的方式。

有类似的吗?如果是的话,你能帮我理解一下吗?

谢谢!

【问题讨论】:

  • 在供应商阻止您之前,您是否询问过您可以提出多少请求。你能把它调到略低于他们的极限吗?
  • 嘿@NigelRen,我没有问他们,我不小心运行了脚本,但这帮助我理解它太多了:D - 是的,我可以一次只运行 10 个每 1-2 分钟通过一次 cronjob,但我认为这个选项在未来不会太聪明和/或可扩展......
  • 可能他们允许每秒 100 个请求或 20 个请求。如果是 100 个,您可以分几批大批量完成,如果他们每秒只允许 20 个请求,那么您可能不得不忍受从长远来看。
  • @NigelRen,但没有其他方法吗?我正在考虑一次运行 10 个请求,但这意味着每秒至少有 8.3(3) 个请求以将其保持在 1 小时内(在我的场景中 - 500 个 url 和 1 小时) - 我不明白他们为什么不提供给我使用 api :( 我讨厌他们 :D
  • 嘿,艾玛,有什么更新吗?我也只是认为您可以简单地使用page = file_get_contents("example.com?id=".$id) 而不是您的卷曲业务;)

标签: php curl optimization


【解决方案1】:

有一个守护进程或 cronjob 以安全的速度不断更新数据库 24/7,当您需要即时结果时,只需查询数据库而不是实际的网站。如果 safe pace 太慢,请继续添加更多 IP(使用代理),直到达到可接受的速度。

【讨论】:

  • 嘿@hanshenrick,问题是库存在分销商网站上更新,而不是在我的网站上,所以为了保持库存更新,我需要一种方法来不断爬取他们的网站(我将留在最后形式 - 我确信它可以改进,但现在它可以工作,当我了解更多关于 curl 的信息时,我会保持这种状态)
【解决方案2】:

更新:

首先我要感谢所有回答我问题的人:D

经过几天的阅读和反复试验,我得出了一个结论;我对此并不完全满意 - 所以我会继续寻找更好的解决方案,但现在这是我的结果:

首先,我在表格中添加了一个新列,每次cron job 运行脚本时,我都会在其中保存time(),我将选择接下来的 30 个未在过去 12 小时 - 我每 10 分钟运行一次 cron job。这是循环:

//get the id's for 30 products that haven't been updated in the past 12 hours
$now = time() - 43200;
$products = $pdo->query("SELECT id FROM products WHERE last_update < $now LIMIT 30");

$bind = [];

foreach($products as $id){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'http://shop.com?product=' . $id[0] . '.html');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);

    preg_match_all('!<span class="stock">(.*?)<\/span>!', $response, $data);
    $stock = $data[1];

    array_push($bind, [$stock, time(), $id[0]]);

    curl_close($ch);
    sleep(2);
}

//then i'm just updating this results trough a query
//i'm using PDO do deal with my db

我认为这里最重要的是sleep 每次看起来都这样我没有得到429,实际上30 产品的执行一次发生得非常快我的意思是大约需要1.5 minutes 才能完成,但我避开了too many requests thing,它由cron job 运行,所以我真的不需要做任何事情。

这种做事方式的局限性在于,如果您有更多的产品可以安装在12 hours 循环中,则使用time() 脚本将简单地从第一个尚未安装的30 产品开始。在过去 12 小时内更新 - 但为了解决这个“问题”,我正在考虑在 db 表中保存一个计数器,以便每次运行该脚本时都可以使用它从 X 开始,然后将其更新为X + 30.

在我看来,使用 curl 抓取网站并不是一种资源明智的解决方案,但它可以让您不再需要抓取。

再说一次,我对我编写这个脚本的方式不是 100% 满意,但现在它可以工作了。

如果我能找到更好的解决方案,我会在这里发布。

谢谢!

【讨论】:

  • SELECT id FROM products WHERE last_update &lt; $now LIMIT 30 这会得到 30 个 random 太旧的东西,但你应该得到 30 个 最旧 太旧的东西,试试SELECT id FROM products WHERE last_update &lt; $now ORDER BY last_update ASC LIMIT 30 - 这会给你30个最古老的东西^^
  • 并且使用 curl 来抓取资源是很好的,但是您在每次迭代时创建和删除一个 curl 实例,这会浪费 cpu 并禁用连接保持连接,如果您重新使用它会更快- 一遍又一遍地使用相同的卷曲手柄,直到你完成。将 curl_init() 移到 foreach 循环上方,并将 curl_close() 移到其下方,它会更快。如果你启用 CURLOPT_ENCODING 会更快
  • 也.. 你没有对$id[0] 进行urlencoding,这可能是一个错误,请尝试'http://shop.com?product=' . urlencode($id[0]) . '.html'。而您正在解析HTML with regex.,这是一种罪过,而且不可靠,请尝试使用$domd=@DOMDocument::loadHTML($response);$xp=new DOMXPath($domd);$stock=$domd-&gt;saveHTML($xp-&gt;query('//span[@class="stock"]')-&gt;item(0));,以防万一&lt;/span&gt; 出现在您要解析的范围内
猜你喜欢
  • 1970-01-01
  • 2018-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多