获取HTTP请求然后处理HTML响应的最佳方式


Best way to fetch HTTP Requests then proccess HTML response

我正在处理一个项目,该项目需要从其他网站导入数据,然后处理此响应,以便能够将其存储在数据库中,并将其与其他结果进行比较。

这些网站中的大多数都不提供json或xml提要,所以我发现自己被迫获取HTML文档。

首先,我开始用CURL获取它们,并用PHP DomDocument&Xpath。但正如我所注意到的,这个过程可能非常缓慢。因此,我开始寻找另一种可能更快、更可靠的解决方案,即使它使用的是另一种语言,如:python、ruby、perl、C等

代码示例:

这只是一个测试代码。在开始项目之前。

初始CURL:

public function curlInit($connId, $url , $postString){
    $this->multiConn[$connId] = curl_init($url);
    curl_setopt($this->multiConn[$connId], CURLOPT_RETURNTRANSFER, true);
    curl_setopt($this->multiConn[$connId], CURLOPT_HEADER, false);
    curl_setopt($this->multiConn[$connId], CURLOPT_ENCODING, 'gzip,deflate');
    curl_setopt($this->multiConn[$connId],CURLOPT_POSTFIELDS, $postString);
}

执行请求:

public function multiExec(){
    $this->multiAddHandler();
    $running = null;
    do {
        curl_multi_exec($this->multiHanler, $running);
    } while ($running);
    $i = 0;
    foreach($this->multiConn as $key => $v){
        $this->multiRespose[$key] = curl_multi_getcontent($v);
    }
}

这是执行请求的代码:

public function parse(){
            // first fetch & process the categories
    $this->getCategoryElementList("li");
            // then fetch all events on each category.
    $this->curl->multiExec();
            // parse the events response
    $this->processEvents();
            // after that i have to parse the details of each event.
}

现在,解析HTML响应:

public function getCategoryElementList($tag){
    foreach($this->categoryIdsArr as $group){
        $domElement = $this->getElementById($group);
        $catList = $domElement->childNodes;
        $this->categoryElementList[] = $catList;
        foreach($catList as $cat){
            // temp Var , to check if the subcat_id is autogenerated, so don't init a curl connection for it
            $autoGenSubCatIds = array();
            if($cat->nodeName == 'li'){
                // -- Getting the category name -- //
                $catNameSapn = $this->searchElement($cat, "span", "class", "nav-special-name");
                if(empty($catNameSapn->item(0)->nodeValue)){
                    $catNameSapn = $this->searchElement($cat, "span", "class", "nav-region-name");
                }
                if(isset($catNameSapn->item(0)->nodeValue)){
                    $_categoryName = $catNameSapn->item(0)->nodeValue;
                }
                // autogenerate subcat_id if not exists 
                if($catNameSapn->item(0)->childNodes->item(0)->nodeName == 'a'){
                    $aTag = $catNameSapn->item(0)->childNodes->item(0)->getAttribute("href");
                    $aTag = split("/",$aTag);
                    $_categoryId = $aTag[4];
                }elseif($catNameSapn->item(0)->childNodes->item(0)->nodeName == '#text'){
                    $tempId = 0;
                    array_walk(str_split($_categoryName), function($value, $index) use (&$tempId){
                        $tempId += ord($value);
                    });
                    $_categoryId = ($tempId);
                    $autoGenSubCatIds[] = $tempId;
                }
                // -- End getting the category name -- //
                $this->arrRes['category'][$_categoryId]['category_name'] = $_categoryName;
                $this->arrRes['category'][$_categoryId]['category_id'] = $_categoryId;
                $subCats = $cat->getElementsByTagName("a");
                foreach($subCats as $subCat){
                    $_subCategroyName = $subCat->nodeValue;
                    $aTag = $subCat->getAttribute("href");
                    $aTag = split("/",$aTag);
                    $_subCategoryId = $aTag[4];
                    $this->arrRes['category'][$_categoryId]['subcat'][$_subCategoryId]['subcat_name'] = $_subCategroyName;
                    $this->arrRes['category'][$_categoryId]['subcat'][$_subCategoryId]['subcat_id'] = $_subCategoryId;
                    if(!in_array($_subCategoryId, $autoGenSubCatIds))
                        $this->curl->curlInit($_subCategoryId, "https://********************.com", "Ids=$_subCategoryId&stId=4&page=0");
                }
            }
        }
    }
}

我的问题不仅仅是关于这个代码。我正在寻找最好的方法来计算连接并解析它们

您描述的过程主要包括三个部分:

  1. 从互联网获取html文档
  2. 解析html
  3. 将解析的结果存储在数据库中

这个过程的第一部分主要取决于你的网络连接、服务器的响应时间等。无论你使用什么语言/技术,都不会有太大区别,因为大部分时间都花在了低级别的系统调用和网络延迟上。

第三部分也主要是关于数据库引擎的性能,即使您可以在这里和那里进行一些优化(如何构建查询、如何管理事务、是否保持持久连接或每次重新连接等)。

这就剩下HTML解析部分了。如果它是格式良好的HTML,那么有几个用C实现的经过优化的解析器,您的语言可以使用(即Python的lxml绑定到libxml2和libxslt)。在编写代码的方式上也有可能进行优化(一般来说,不是说我没有读过的上面的代码片段),但如何做到这一点取决于您使用的确切语言/技术。

现在的重点是,无论解析器和您自己的代码如何优化,您仍然会受到网络和数据库性能的约束。在这里获得更好性能的唯一方法是尽可能多地并行化进程,尽可能多的节点上的HTTP客户端为尽可能多节点上的解析器提供所需的信息,而解析器本身则为数据库提供信息。

我们已经在Python应用程序中解决了一个非常类似的问题,使用Celery和RabbitMQ进行并行化,在4个不同的"工作"节点上平衡负载(加上一个用于数据库,一个用于Django/apache前端),但在大多数语言/技术中都有同样好甚至更好的解决方案(MapReduce有人吗?)。