浅谈数据抓取的几种方法

Posted 北晨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈数据抓取的几种方法相关的知识,希望对你有一定的参考价值。

在下抓数据也小有研究,现分享几个自己研究出来的抓数据的技术,可能会有很多不足的地方,欢迎大家指正补充哈哈!


方法一:直接抓取网页源码
优点:速度快。
缺点:1,正由于速度快,易被服务器端检测,可能会限制当前ip的抓取。对于这点,可以尝试使用ip代码解决。
   2,如果你要抓取的数据,是在网页加载完后,js修改了网页元素,无法抓取。
   3,遇到抓取一些大型网站,如果需要抓取如登录后的页面,可能需要破解服务器端帐号加密算法以及各种加密算法,及其考验技术性。
适用场景:网页完全静态化,并且你要抓取的数据在网页首次加载完成就加载出来了。涉及登录或者权限操作的类似页面未做任何帐号加密或只做简单加密的。
当然,如果该网页你抓取的数据,是通过接口获得的json,那么,你就更幸福的,直接抓取json页面即可。
对于有登录的页面,我们如何拿到他的登录页之后的源码呢?
首先我要介绍一下,对于session保存帐号信息的情况下,服务器是如何确定该用户身份的。
首先,用户登录成功后,服务器端会将用户的当前会话信息保存到session中,每一个session有一个唯一标志sessionId。则用户访问这个页面,session被创建后,就会接收到服务器端传回的sessionId,并将其保存到cookie中,因此,我们可以用chrome浏览器打开检查项,查看当前页面的jsessionId。下次用户访问需要登录的页面时,用户发送的请求头会附上这个sessionId,服务器端通过这个sessionId就可以确定用户的身份。
这里,我搭建了一个简单的jsp登录页面,登录后的帐号信息保存在服务器端session中。
思路:1,登录。2,登录成功后获得cookie。3,将cookie放到请求头中,向登录页发送请求。
附上java版本的代码及python
java版:

package craw;  
   
import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintStream;  
import java.io.PrintWriter;  
import java.net.HttpURLConnection;  
import java.net.MalformedURLException;  
import java.net.URL;  
import java.net.URLConnection;  
import java.util.List;  
import java.util.Map;  
   
public class CrawTest {  
    //获得网页源代码  
    private static String gethtml(String urlString,String charset,String cookie){  
        StringBuffer html = new StringBuffer();  
        try {  
            URL url = new URL(urlString);  
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();  
            urlConn.setRequestProperty("Cookie", cookie);  
            BufferedReader br = new BufferedReader(new InputStreamReader(urlConn.getInputStream(),charset));  
               
            String str;  
            while((str=br.readLine())!=null){  
                html.append(str);  
            }  
        } catch (MalformedURLException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
           
        return html.toString();  
    }  
       
       
    //发送post请求,并返回请求后的cookie  
    private static String postGetCookie(String urlString,String params,String charset){  
        String cookies=null;  
        try {  
            URL url = new URL(urlString);  
            URLConnection urlConn = url.openConnection();  
            urlConn.setDoInput(true);  
            urlConn.setDoOutput(true);  
            PrintWriter out = new PrintWriter(urlConn.getOutputStream());  
            out.print(params);  
            out.flush();  
            cookies = urlConn.getHeaderFields().get("Set-Cookie").get(0);  
        } catch (MalformedURLException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        return cookies;  
    }  
       
    public static void main(String[] args) {  
        String cookie = postGetCookie("http://localhost:8080/loginDemo/login",  
                "username=admin&password=123456","utf-8");  
        String html = getHtml("http://localhost:8080/loginDemo/index.jsp", "utf-8", cookie);  
        System.out.println(html);//这里我们就可能输出登录后的网页源代码了  
    }  
}  

  python版:

#encoding:utf-8  
import urllib  
import urllib2  
   
data={\'username\':\'admin\',\'password\':\'123456\'}  
data=urllib.urlencode(data)  
response = urllib2.urlopen(\'http://localhost:8080/loginDemo/login\',data=data)//登录  
cookie = response.info()[\'set-cookie\']//获得登录后的cookie  
jsessionId = cookie.split(\';\')[0].split(\'=\')[-1]//从cookie获得sessionId  
html = response.read()  
if html==\'error\':  
    print(\'用户名密码错误!\')  
elif html==\'success\':  
    headers = {\'Cookie\':\'JSESSIONID=\'+jsessionId}//请求头  
    request =urllib2.Request(\'http://localhost:8080/loginDemo/index.jsp\',headers=headers)  
    html = urllib2.urlopen(request).read()  
    print(html)//输出登录后的页面源码  

我们可以很明显看出python的优势,会比java少写至少一半的代码量,当然这即是java的优点,也是java的缺点,优点就是更加灵活,程序员可以更好的控制底层代码的实现,缺点就是不易上手,对技术要求过高,因此,如果你是一名数据抓取新人,个人极力推荐你学习python。


方法二:模拟浏览器操作
优点:1,和用户操作相类似,不易被服务器端检测。
    2,对于登录的网站,即使是经过了N层加密的,无需考虑其加密算法。
    3,可随时获得当前页面各元素最新状态。
缺点:1,速度稍慢。
   
这里介绍几个不错的模拟浏览器操作的类库:
C# webbrower控件:
如果你研究过c# winform,相信你绝对不会对webbrower这个控件陌生,他就是一个浏览器,内部驱动其实也是ie的驱动。
他可以以dom方式随时解析当前的document(网页文档对象),不仅可以拿到相关Element对象,还可以对element对象进行修改,乃至调用方法,如onclick方法,onsubmit等等,也可以直接调用页面js方法。

C#对webbrower操作代码:

webBrowser1.Navigate("https://localhost//index.html");//加载一个页面  
 
需要注意的是:不要直接执行以下代码,因为网页加载需要时间,建议以下代码写到webBrowser1_DocumentCompleted(加载完成)事件中:  
webBrowser1.Document.GetElementById("username").InnerText="admin";//在网页中找到id为username的元素,设置其文本为admin  
 webBrowser1.Document.GetElementById("password").InnerText = "123456";//在网页中找到id为password的元素,设置其文本为123456  
   
  webBrowser1.Document.InvokeScript("loginEncrypt");//调用网页js函数:loginEncrypt.

由于有些页面ie可能不够友好,或者如果ie版本过低,甚至是安全证书等问题,那么这个方案可能就pass掉了。
我们可以直接使用selenium库来操作系统中真实浏览器,如chrome浏览器,selenuim支持多语言开发,以python调用selenium为例,selenium就是直接操作我们系统中的浏览器,但需要确保浏览器安装对应的驱动。
然而,真正开发中,有时我们可能不希望看到这个浏览器界面,这里,我可以推荐大家一个后台浏览器,他直接在cmd中进行操作,没有界面,那就是phantomjs。
这样,我们使用python+selenium+phantomjs就可以模拟浏览器的操作,并且看不到界面,由于phantomjs没有界面,所以他会比一般的浏览器要快很多。
这里网上有大量的资料,一时半会也讲不请,这里就不做过多讲解,大家可以看看:http://www.cnblogs.com/luxiaojun/p/6144748.html

三,Fidder script:
fidder是一款非常强大的数据抓取工具,他不仅可以抓取到当前系统中的http请求,他还可以提供安全证书,因此有时候,我们抓取过程中,如果遇到安全证书错误,我们不妨把fidder打开,让他提供你一个证书,或许你就近成功近在咫尺。
令人更加强大的是fidder script,他可以在对抓取到了请求后,进行一系统操作,如将请求到的数据保存到硬盘中。或者在请求前,修改请求头,可谓是抓取一利器,这样,我们使用fidder配合上之前的各类方法,相信可以解决大多难题。并且他的语法和C like系列语法相似,类库和c#大多相同,相信对C#熟悉的同学,上手fiddler script会很快。
fidder script:http://blog.csdn.net/crisschan/article/details/45602745

以上是关于浅谈数据抓取的几种方法的主要内容,如果未能解决你的问题,请参考以下文章

php抓取页面的几种方式

Laravel:如何在控制器的几种方法中重用代码片段

iOS之浅谈纯代码控制UIViewController视图控制器跳转界面的几种方法

浅谈 JS 的数据遍历的几种方式

浅谈http的几种请求方法

浅谈java开启异步线程的几种方法(@Async,AsyncManager,线程池)