【问题标题】:Scrape web page contents抓取网页内容
【发布时间】:2010-10-09 17:57:00
【问题描述】:

我正在开发一个项目,我想在后台抓取网站的内容并从该抓取的网站中获取一些有限的内容。例如,在我的页面中,我有“userid”和“password”字段,通过使用这些字段,我将访问我的邮件并抓取我的收件箱内容并将其显示在我的页面中。

我单独使用 javascript 完成了上述操作。但是,当我单击登录按钮时,我的页面 (http://localhost/web/Login.html) 的 URL 更改为我被抓取的 URL (http://mail.in.com/mails/inbox.php?nomail=....)。但是我在不更改网址的情况下取消了详细信息。

【问题讨论】:

标签: php curl web-scraping httprequest screen-scraping


【解决方案1】:

您可以使用 PHP 的 cURL 扩展从您的 PHP 页面脚本中向另一个网站发出 HTTP 请求。见the documentation here.

当然,这里的缺点是您的网站会响应缓慢,因为您必须先抓取外部网站,然后才能将完整的页面/输出呈现给您的用户。

【讨论】:

  • 我废弃了页面内容,但它会将整个页面重定向到新的 url。但我只想要页面内容..
  • 我认为从客户端 Javascript 中抓取另一个页面的 HTML 不是一个好主意。最好在服务器端,在 PHP 本身内完成。
【解决方案2】:

一定要选择PHP Simple HTML DOM Parser。它快速、简单且超级灵活。它基本上将整个 HTML 页面粘贴到一个对象中,然后您可以访问该对象中的任何元素。

像官网的例子一样,获取谷歌主页上的所有链接:

// Create DOM from URL or file
$html = file_get_html('http://www.google.com/');

// Find all images 
foreach($html->find('img') as $element) 
       echo $element->src . '<br>';

// Find all links 
foreach($html->find('a') as $element) 
       echo $element->href . '<br>';

【讨论】:

  • 貌似不支持post参数。
【解决方案3】:

您是否尝试过 OutWit Hub?这是一个完整的抓取环境。您可以让它尝试猜测结构或开发自己的刮板。我真的建议你看看它。它让我的生活变得简单多了。 ZR

【讨论】:

    【解决方案4】:

    HTTP 请求

    首先,您发出 HTTP 请求以获取页面内容。有几种方法可以做到这一点。

    打开

    发送 HTTP 请求最基本的方法是使用fopen。一个主要优点是您可以设置一次读取多少个字符,这在读取非常大的文件时很有用。不过,这并不是最容易正确完成的事情,除非您正在读取非常大的文件并且担心遇到内存问题,否则不建议您这样做。

    $fp = fopen("http://www.4wtech.com/csp/web/Employee/Login.csp", "rb");
    if (FALSE === $fp) {
        exit("Failed to open stream to URL");
    }
    
    $result = '';
    
    while (!feof($fp)) {
        $result .= fread($fp, 8192);
    }
    fclose($fp);
    echo $result;
    

    file_get_contents

    最简单的方法就是使用file_get_contents。 if 和 fopen 差不多,但是你有更少的选择。这里的一个主要优点是它只需要一行代码。

    $result = file_get_contents('http://www.4wtech.com/csp/web/Employee/Login.csp');
    echo $result;
    

    套接字

    如果您需要对发送到服务器的标头进行更多控制,您可以结合使用套接字和fopen

    $fp = fsockopen("www.4wtech.com/csp/web/Employee/Login.csp", 80, $errno, $errstr, 30);
    if (!$fp) {
        $result = "$errstr ($errno)<br />\n";
    } else {
        $result = '';
        $out = "GET / HTTP/1.1\r\n";
        $out .= "Host: www.4wtech.com/csp/web/Employee/Login.csp\r\n";
        $out .= "Connection: Close\r\n\r\n";
        fwrite($fp, $out);
        while (!feof($fp)) {
            $result .= fgets($fp, 128);
        }
        fclose($fp);
    }
    echo $result;
    

    或者,您也可以使用流。 Streams 类似于套接字,可以与fopenfile_get_contents 结合使用。

    $opts = array(
      'http'=>array(
        'method'=>"GET",
        'header'=>"Accept-language: en\r\n" .
                  "Cookie: foo=bar\r\n"
      )
    );
    
    $context = stream_context_create($opts);
    
    $result = file_get_contents('http://www.4wtech.com/csp/web/Employee/Login.csp', false, $context);
    echo result;
    

    卷曲

    如果您的服务器支持 cURL(通常支持),建议使用 cURL。使用 cURL 的一个关键优势是它依赖于其他编程语言中常用的流行 C 库。它还提供了一种方便的方法来创建请求标头,并自动解析响应标头,并提供简单的界面以防出错。

    $defaults = array( 
        CURLOPT_URL, "http://www.4wtech.com/csp/web/Employee/Login.csp"
        CURLOPT_HEADER=> 0
    );
    
    $ch = curl_init(); 
    curl_setopt_array($ch, ($options + $defaults)); 
    if( ! $result = curl_exec($ch)) { 
        trigger_error(curl_error($ch)); 
    } 
    curl_close($ch); 
    echo $result; 
    

    或者,您可以使用many PHP libraries 之一。不过,我不建议使用库,因为它可能会矫枉过正。在大多数情况下,您最好在底层使用 cURL 编写自己的 HTTP 类。


    HTML 解析

    PHP 有一种方便的方法可以将任何 HTML 加载到 DOMDocument

    $pagecontent = file_get_contents('http://www.4wtech.com/csp/web/Employee/Login.csp');
    $doc = new DOMDocument();
    $doc->loadHTML($pagecontent);
    echo $doc->saveHTML();
    

    不幸的是,PHP 对 HTML5 的支持是有限的。如果您在尝试解析页面内容时遇到错误,请考虑使用第三方库。为此,我可以推荐Masterminds/html5-php。使用此库解析 HTML 文件与使用 DOMDocument 解析 HTML 文件非常相似。

    use Masterminds\HTML5;
    
    $pagecontent = file_get_contents('http://www.4wtech.com/csp/web/Employee/Login.csp');
    $html5 = new HTML5();
    $dom = $html5->loadHTML($html);
    echo $html5->saveHTML($dom);
    

    或者,您可以使用例如。我的图书馆PHPPowertools/DOM-Query。它在后台使用自定义版本的Masterminds/html5-php 将HTML5 字符串解析为DomDocumentsymfony/DomCrawler,以便将CSS 选择器转换为XPath 选择器。它始终使用相同的DomDocument,即使将一个对象传递给另一个对象,以确保良好的性能。

    namespace PowerTools;
    
    // Get file content
    $pagecontent = file_get_contents( 'http://www.4wtech.com/csp/web/Employee/Login.csp' );
    
    // Define your DOMCrawler based on file string
    $H = new DOM_Query( $pagecontent );
    
    // Define your DOMCrawler based on an existing DOM_Query instance
    $H = new DOM_Query( $H->select('body') );
    
    // Passing a string (CSS selector)
    $s = $H->select( 'div.foo' );
    
    // Passing an element object (DOM Element)
    $s = $H->select( $documentBody );
    
    // Passing a DOM Query object
    $s = $H->select( $H->select('p + p') );
    
    // Select the body tag
    $body = $H->select('body');
    
    // Combine different classes as one selector to get all site blocks
    $siteblocks = $body->select('.site-header, .masthead, .site-body, .site-footer');
    
    // Nest your methods just like you would with jQuery
    $siteblocks->select('button')->add('span')->addClass('icon icon-printer');
    
    // Use a lambda function to set the text of all site blocks
    $siteblocks->text(function( $i, $val) {
        return $i . " - " . $val->attr('class');
    });
    
    // Append the following HTML to all site blocks
    $siteblocks->append('<div class="site-center"></div>');
    
    // Use a descendant selector to select the site's footer
    $sitefooter = $body->select('.site-footer > .site-center');
    
    // Set some attributes for the site's footer
    $sitefooter->attr(array('id' => 'aweeesome', 'data-val' => 'see'));
    
    // Use a lambda function to set the attributes of all site blocks
    $siteblocks->attr('data-val', function( $i, $val) {
        return $i . " - " . $val->attr('class') . " - photo by Kelly Clark";
    });
    
    // Select the parent of the site's footer
    $sitefooterparent = $sitefooter->parent();
    
    // Remove the class of all i-tags within the site's footer's parent
    $sitefooterparent->select('i')->removeAttr('class');
    
    // Wrap the site's footer within two nex selectors
    $sitefooter->wrap('<section><div class="footer-wrapper"></div></section>');
    

    【讨论】:

    • 被低估的答案
    • PHP 对 HTML5 的支持如何受到限制?!它仍然有限吗?
    • @Anthony : PHP 内置的工具是为 HTML 4 和 XML 设计的。当您尝试使用它们来解析或编写 HTML5 时,这会导致各种问题。据我所知,自从我写下这个答案以来,这并没有改变。
    猜你喜欢
    • 2019-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 2020-05-24
    相关资源
    最近更新 更多