`
m635674608
  • 浏览: 5021894 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

htmlunit

 
阅读更多

 做Java编程的人其实,很多不懂SEO,也不知道如何让百度收录等等,当然,对于爬虫,网页抓取,也不是所有编程的人有考虑到,但是专门做这个人,还是需要了解一下这里面的知识的,那下面就来介绍一下一些爬虫和网页采集相关的组件和插件吧~

大家在做爬虫、网页采集、通过网页自动写入数据时基本上都接触过这两个组件(权且称之为组件吧),网上入门资料已经很多了,我想从实际的应用角度谈谈我对于这两个组件的看法,并记录在博客中,以便日后翻阅,欢迎大家批评指正。 

 

        本文主要比较两者的优劣性以及介绍应用中的使用技巧,推荐一些入门资料以及非常实用的辅助工具,希望能对大家有所帮助。 

 

        大家有任何疑问或者建议希望留言给我,一起交流学习。 

 

下面我们首先来看下2个组件的区别和优劣性: 

 

HtmlUnit: 

 

 

HtmlUnit本来是一款自动化测试的工具,它采用了HttpClient和java自带的网络api结合来实现,它与HttpClient的不同之处在于,它比HttpClient更“人性化”。 

 

在写HtmlUnit代码的时候,仿佛感觉到的就是在操作浏览器而非写代码,得到页面(getPage)– 寻找到文本框(getElementByID || getElementByName || getElementByXPath 等等)– 输入文字(type,setValue,setText等等)– 其他一些类似操作 — 找到提交按钮 — 提交 — 得到新的Page,这样就非常像一个人在后台帮你操作浏览器一样,而你要做的就是告诉他如何操作以及需要填入哪些值。 

 

      优点: 

 

         一、网页的模拟化 

 

 

首先说说HtmlUnit相对于HttpClient的最明显的一个好处,HtmlUnit更好的将一个网页封装成了一个对象,如果你非要说HttpClient返回的接口HttpResponse实际上也是存储了一个对象那也可以,但是HtmlUnit不仅保存了这个网页对象,更难能可贵的是它还存有这个网页的所有基本操作甚至事件。这就是说,我们对于操作这个网页可以像在jsp中写js一样,这是非常方便的,比如:你想某个节点的上一个节点,查找所有的按钮,查找样式为“bt-style”的所有元素,对于某些元素先进行一些改造,然后再转成String,或者我直接得到这个网页之后操作这个网页,完成一次提交都是非常方便的。这意味着你如果想分析一个网页会来的非常的容易,比如我附上一段百度新闻高级搜索的代码: 

 

         

// 得到浏览器对象,直接New一个就能得到,现在就好比说你得到了一个浏览器了 

WebClient webclient = new WebClient(); 

 

// 这里是配置一下不加载css和javaScript,配置起来很简单,是不是 

webclient.getOptions().setCssEnabled(false); 

webclient.getOptions().setJavaScriptEnabled(false); 

 

// 做的第一件事,去拿到这个网页,只需要调用getPage这个方法即可 

HtmlPage htmlpage = webclient.getPage(“http://news.baidu.com/advanced_news.html”); 

 

// 根据名字得到一个表单,查看上面这个网页的源代码可以发现表单的名字叫“f” 

final HtmlForm form = htmlpage.getFormByName(“f”); 

// 同样道理,获取”百度一下“这个按钮 

final HtmlSubmitInput button = form.getInputByValue(“百度一下”); 

// 得到搜索框 

final HtmlTextInput textField = form.getInputByName(“q1″); 

// 最近周星驰比较火呀,我这里设置一下在搜索框内填入”周星驰“ 

textField.setValueAttribute(“周星驰”); 

// 输入好了,我们点一下这个按钮 

final HtmlPage nextPage = button.click(); 

// 我把结果转成String 

String result = nextPage.asXml(); 

 

System.out.println(result); 

 

 

然后你可以把得到的result结果复制到一个文本,然后用浏览器打开该文本,是不是想要的东西(如图结),很简单对吧,为什么会感觉简单,因为它完全符合我们操作浏览器的习惯,当然最终它也是用HttpClient和其它一些工具类实现的,但是这样的封装是非常人性化和令人惊叹的。 

 

 

Htmlunit可以有效的分析出 dom标签,并且可以有效的运行页面上的js以便得到一些需要执行JS才能得到的值,你仅仅需要做的就是执行executeJavaScript()这个方法而已,这些都是HtmlUnit帮我们封装好,我们要做的仅仅是告诉它需要做什么。 

                WebClient webclient = new WebClient(); 

HtmlPage htmlpage = webclient.getPage(“you url”); 

htmlpage.executeJavaScript(“the function name you want to execute”); 

 

 

对于使用Java程序员来说,对对象的操作就再熟悉不过了,HtmlUnit所做的正是帮我们把网页封装成一个对象,一个功能丰富的,透明的对象。 

 

 

二、网络响应的自动化处理 

 

 

HtmlUnit拥有强大的响应处理机制,我们知道:常见的404是找不到资源,100等是继续,300等是跳转…我们在使用HttpClient的时候它会把响应结果告诉我们,当然,你可以自己来判断,比如说,你发现响应码是302的时候,你就在响应头去找到新的地址并自动再跳过去,发现是100的时候就再发一次请求,你如果使用HttpClient,你可以这么去做,也可以写的比较完善,但是,HtmlUnit已经较为完整的实现了这一功能,甚至说,他还包括了页面JS的自动跳转(响应码是200,但是响应的页面就是一个JS),天涯的登录就是这么一个情况,让我们一起来看下。 

 

                  

/** 

* @author CaiBo 

* @date 2014年9月15日 上午9:16:36 

* @version $Id$ 

*/ 

public class TianyaTest { 

/** 

*/ 

public static void main(String[] args) throws Exception { 

// 这是一个测试,也是为了让大家看的更清楚,请暂时抛开代码规范性,不要纠结于我多建了一个局部变量等 

// 得到认证https的浏览器对象 

HttpClient client = getSSLInsecureClient(); 

// 得到我们需要的post流 

HttpPost post = getPost(); 

// 使用我们的浏览器去执行这个流,得到我们的结果 

HttpResponse hr = client.execute(post); 

// 在控制台输出我们想要的一些信息 

showResponseInfo(hr); 

 

private static void showResponseInfo(HttpResponse hr) throws ParseException, IOException { 

 

System.out.println(“响应状态行信息:” + hr.getStatusLine()); 

System.out.println(“—————————————————————”); 

 

System.out.println(“响应头信息:”); 

Header[] allHeaders = hr.getAllHeaders(); 

for (int i = 0; i < allHeaders.length; i++) { 

System.out.println(allHeaders[i].getName() + “:” + allHeaders[i].getValue()); 

 

System.out.println(“—————————————————————”); 

System.out.println(“响应正文:”); 

System.out.println(EntityUtils.toString(hr.getEntity())); 

 

 

// 得到一个认证https链接的HttpClient对象(因为我们将要的天涯登录是Https的) 

// 具体是如何工作的我们后面会提到的 

private static HttpClient getSSLInsecureClient() throws Exception { 

// 建立一个认证上下文,认可所有安全链接,当然,这是因为我们仅仅是测试,实际中认可所有安全链接是危险的

SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { 

public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { 

return true; 

}).build(); 

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); 

return HttpClients.custom().// 

setSSLSocketFactory(sslsf)// 

// .setProxy(new HttpHost(“127.0.0.1″, 8888)) 

.build(); 

 

// 获取我们需要的Post流,如果你是把我的代码复制过去,请记得更改为你的用户名和密码 

private static HttpPost getPost() { 

HttpPost post = new HttpPost(“https://passport.tianya.cn/login”); 

 

// 首先我们初始化请求头 

post.addHeader(“Referer”, “https://passport.tianya.cn/login.jsp”); 

post.addHeader(“Host”, “passport.tianya.cn”); 

post.addHeader(“Origin”, “http://passport.tianya.cn”); 

 

// 然后我们填入我们想要传递的表单参数(主要也就是传递我们的用户名和密码) 

// 我们可以先建立一个List,之后通过post.setEntity方法传入即可 

// 写在一起主要是为了大家看起来方便,大家在正式使用的当然是要分开处理,优化代码结构的 

List<NameValuePair> paramsList = new ArrayList<NameValuePair>(); 

/* 

* 添加我们要的参数,这些可以通过查看浏览器中的网络看到,如下面我的截图中看到的一样 

* 不论你用的是firebut,httpWatch或者是谷歌自带的查看器也好,都能查看到(后面会推荐辅助工具来查看) 

* 要把表单需要的参数都填齐,顺序不影响 

*/ 

paramsList.add(new BasicNameValuePair(“Submit”, “”)); 

paramsList.add(new BasicNameValuePair(“fowardURL”, “http://www.tianya.cn”)); 

paramsList.add(new BasicNameValuePair(“from”, “”)); 

paramsList.add(new BasicNameValuePair(“method”, “name”)); 

paramsList.add(new BasicNameValuePair(“returnURL”, “”)); 

paramsList.add(new BasicNameValuePair(“rmflag”, “1″)); 

paramsList.add(new BasicNameValuePair(“__sid”, “1#1#1.0#a6c606d9-1efa-4e12-8ad5-3eefd12b8254″)); 

 

// 你可以申请一个天涯的账号 并在下两行代码中替换为你的用户名和密码 

paramsList.add(new BasicNameValuePair(“vwriter”, “ifugletest2014″));// 替换为你的用户名 

paramsList.add(new BasicNameValuePair(“vpassword”, “test123456″));// 你的密码 

 

// 将这个参数list设置到post中 

post.setEntity(new UrlEncodedFormEntity(paramsList, Consts.UTF_8)); 

return post; 

 

 

 

执行上面这个Main函数你会得到一下的结果: 

 

 

我们看到,响应码确实是200,表明成功了,其实这个响应相当于是302,它是需要跳转的,只不过它的跳转写到了body部分的js里面而已。 

 

<script> 

location.href=”http://passport.tianya.cn:80/online/loginSuccess.jsp?fowardurl=http%3A%2F%2Fwww.tianya.cn%2F94693372&userthird=&regOrlogin=%E7%99%BB%E5%BD%95%E4%B8%AD……&t=1410746182629&k=8cd4d967491c44c5eab1097e0f30c054&c=6fc7ebf8d782a07bb06624d9c6fbbf3f”;

</script> 

 

 

它这是一个页面上的跳转 

 

那这个时候如果你使用HttpClient就头疼了(当然也是可以处理的,后面讲到)。如果你使用的是HtmlUnit,整个过程显得简单轻松。 

 

public class TianyaTestByHtmlUnit { 

public static void main(String[] args) throws Exception { 

 

WebClient webClient = new WebClient(); 

// 拿到这个网页 

HtmlPage page = webClient.getPage(“http://passport.tianya.cn/login.jsp”); 

 

// 填入用户名和密码 

HtmlInput username = (HtmlInput) page.getElementById(“userName”); 

username.type(“ifugletest2014″); 

HtmlInput password = (HtmlInput) page.getElementById(“password”); 

password.type(“test123456″); 

 

// 提交 

HtmlButton submit = (HtmlButton) page.getElementById(“loginBtn”); 

HtmlPage nextPage = submit.click(); 

System.out.println(nextPage.asXml()); 

 

这样简单的几行代码就完成了。 

      

 

三、并行控制 和串行控制 

 

        既然HtmlUnit封装了那么多的底层api和hHttpClient操作,那么它有没有给我们提供自定义各种响应策略和监听整个执行过程的方法呢?,答案是肯定的。由于HtmlUnit提供的监听和控制方法比较多,我说几个大家可能接触比较少,但很有用的方法。其他的类似于:设置CSS有效,设置不抛出JS异常,设置使用SSL安全链接,诸如此类,大家通过webClient.getOptions().set***,就可以设置了,这种大家都比较熟了。 

 

        (1)首先来看一下JS错误处理监听机制,我们打开HtmlUnit源码可以看到(该源码位置在JavaScriptEngine类中的handleJavaScriptException方法处) 

 

 

  protected void handleJavaScriptException(final ScriptException scriptException, final boolean triggerOnError) { 

        // Trigger window.onerror, if it has been set. 

        final HtmlPage page = scriptException.getPage(); 

        if (triggerOnError && page != null) { 

            final WebWindow window = page.getEnclosingWindow(); 

            if (window != null) { 

                final Window w = (Window) window.getScriptObject(); 

                if (w != null) { 

                    try { 

                        w.triggerOnError(scriptException); 

                    } 

                    catch (final Exception e) { 

                        handleJavaScriptException(new ScriptException(page, e, null), false); 

                    } 

                } 

            } 

        } 

        // 这里尝试去取我们设置的JavaScript错误处理器 

        final JavaScriptErrorListener javaScriptErrorListener = getWebClient().getJavaScriptErrorListener(); 

        if (javaScriptErrorListener != null) { 

            javaScriptErrorListener.scriptException(page, scriptException); 

        } 

        // Throw a Java exception if the user wants us to. 

        if (getWebClient().getOptions().isThrowExceptionOnScriptError()) { 

            throw scriptException; 

        } 

        // Log the error; ScriptException instances provide good debug info. 

        LOG.info(“Caught script exception”, scriptException); 

    } 

 

也就是说我们它在发现JS错误的时候会自动去寻找我们是否有处理器,有的话就会用我们设置的处理器来处理,要在webClient里加一个处理器也非常的方便。使用: 

 

webClient.setJavaScriptErrorListener(new 你自己的JavaScriptErrorListener());即可。自己的JavaScriptErrorListener也很好实现,直接继承JavaScriptErrorListener接口即可,然后你就可以在javaScript出错时自行处理,你可以选择分析它的url、修正它的url、重新再获取或者直接忽略等等。有js错误处理器,当然也还有别的了,这一类型的我就只说一个了。为了防止有小白不明白,我还是贴出一个简单的实现好了。 

 

/** 

* @author CaiBo 

* @date 2014年8月12日 上午12:32:08 

* @version $Id: WaiJavaScriptErrorListener.java 3943 2014-08-12 03:54:25Z CaiBo $ 

*/ 

public class WaiJavaScriptErrorListener implements JavaScriptErrorListener { 

 

public WaiJavaScriptErrorListener() { 

 

 

@Override 

public void scriptException(HtmlPage htmlPage, ScriptException scriptException) { 

// TODO Auto-generated method stub 

 

 

@Override 

public void timeoutError(HtmlPage htmlPage, long allowedTime, long executionTime) { 

// TODO Auto-generated method stub 

 

 

@Override 

public void malformedScriptURL(HtmlPage htmlPage, String url, MalformedURLException malformedURLException) { 

// TODO Auto-generated method stub 

 

 

@Override 

public void loadScriptError(HtmlPage htmlPage, URL scriptUrl, Exception exception) { 

// TODO Auto-generated method stub 

 

 

public static void main(String[] args) { 

WebClient webClient = new WebClient(); 

webClient.setJavaScriptErrorListener(new WaiJavaScriptErrorListener()); 

Main方法处实现了JS错误自定义处理的webClient 

 

 

 

(2)链接响应监听 

 

       很多时候我们想看看HtmlUnit到底去拿了什么东西,或者说我想对它拿的东西过滤一下,再或者我想把它拿到的某些东西存起来,那这个时候响应监听就很必要了。比如说一个最简单的响应监听。 

 

/** 

* @author CaiBo 

* @date 2014年9月15日 上午10:59:30 

* @version $Id$ 

*/ 

public class SimpleConectionListener extends FalsifyingWebConnection { 

 

private static final Logger LOGGER = LoggerFactory.getLogger(SimpleConectionListener.class); 

 

public SimpleConectionListener(WebClient webClient) throws IllegalArgumentException { 

super(webClient); 

 

@Override 

public WebResponse getResponse(WebRequest request) throws IOException { 

// 得到了这个响应,你想怎么处理就怎么处理了,不多写了 

 

WebResponse response = super.getResponse(request); 

String url = response.getWebRequest().getUrl().toString(); 

 

if (LOGGER.isDebugEnabled()) { 

LOGGER.debug(“下载文件链接:” + url); 

if (check(url)) { 

return createWebResponse(response.getWebRequest(), “”, “application/javascript”, 200, “Ok”); 

return response; 

 

private boolean check(String url) { 

// TODO 加入你自己的判断什么的 

return false; 

 

这样我们就实现了一个自己的监听器,虽然比较简陋。现在我们把它设置到我们的webClient里面去。 

 

                WebClient webClient = new WebClient(); 

// 如果你好奇这里仅仅传进去没有返回,怎么webClient就改变了,你可以到这个实例化里面看下就明白了 

new WebConnectionListener(webClient); 

// 这个webClient在上一步之后,已经被监听了 

webClient.getPage(“someUrl”); 

 

 

结果就如上图所示了。 

 

HtmlUnit还有其他许多并、串行控制方法,统一cookie,统一连接池等等,就不一一叙述了。 

 

四、强大的缓存机制 

 

        为什么第一次获取一个网页可能会比较慢,但是第二次来拿就特别快呢?在HtmlUnit源码webClient类中的loadWebResponseFromWebConnection方法中我们可以看到。 

 

final WebResponse fromCache = getCache().getCachedResponse(webRequest); 

        final WebResponse webResponse; 

        if (fromCache != null) { 

            webResponse = new WebResponseFromCache(fromCache, webRequest); 

        } 

        else { 

            try { 

                webResponse = getWebConnection().getResponse(webRequest); 

            } 

            catch (final NoHttpResponseException e) { 

                return new WebResponse(responseDataNoHttpResponse_, webRequest, 0); 

            } 

            getCache().cacheIfPossible(webRequest, webResponse, null); 

        } 

 

当然,它还有许多别的缓存机制来加快我们的访问速度,减少带宽压力。 

 

 

劣势: 

 

       相对于HttpClient来说,HtmlUnit的优点大致就这么多了,那相对于HttpClient来说,短程距离上(访问量小的情况下),HtmlUnit的性能是不如HttpClient的,这也很容易理解,HtmlUnit把HttpClient封装了一层嘛,在短程距离行不如HttpClient就很正常了,在具体的业务下,那就要看程序员水平了。 

 

 

写太长我自己容易疏忽,大家看着也累,所以第一篇就只谈一下HtmlUnit的优势和劣势了,下一篇将讲述HttpClient的优势和劣势,之后再对他们进行详细比较以及介绍技巧。

分享到:
评论

相关推荐

    Htmlunit2.23-bin.zip

    HTMLUnit是一个强大的Java库,它模拟了一个无头Web浏览器,主要用于自动化测试和网页抓取。在版本2.23的zip文件中,我们主要关注HTMLUnit的核心功能和它如何帮助开发者处理HTML内容。 HTMLUnit的核心是基于Jakarta ...

    htmlunit依赖jar包

    HTMLUnit是一款功能强大的Java库,它模拟了一个无头Web浏览器,允许开发者在没有真实浏览器环境的情况下执行JavaScript,处理Ajax请求,以及与网页进行交互。这个库对于自动化测试、数据抓取和网页爬虫项目非常有用...

    htmlUnit所需jar包

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化Web页面的导航和交互。这个库在测试、数据抓取以及不需图形用户界面的场景中非常有用。"htmlUnit所需jar包"的标题表明这是一个包含HTMLUnit运行所需...

    htmlunit基本jar包

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化Web页面的测试和交互。这个"htmlunit基本jar包"包含了运行HTMLUnit所需的所有核心组件,无需通过Maven来管理和依赖。这意味着用户可以直接在项目中...

    htmlunit 及其 依赖包

    HTMLUnit是一个功能强大的Java库,它模拟了一个无头Web浏览器,允许开发者进行自动化网页测试和网络爬虫工作。这个库的核心在于它能够解析HTML、执行JavaScript,并与网页上的元素进行交互,而无需真实地打开一个...

    htmlunit依赖的所有jar

    HTMLUnit是一个Java库,模拟一个无头Web浏览器,主要用于自动化测试和网页抓取。它能够解析HTML、执行JavaScript,并返回用户可以操作的DOM元素。在Java应用中使用HTMLUnit,通常需要依赖一系列的JAR(Java Archive...

    htmlunit所需要jar包

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化网页测试和网页抓取。这个库允许开发者在没有实际浏览器的情况下与HTML页面进行交互,执行JavaScript,并获取页面加载后的结果。它对于进行功能测试...

    com.gargoylesoftware.htmlunit-2.29所需包

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化Web页面的导航和交互。这个库在进行网络爬虫或自动化测试时非常有用,因为它可以解析HTML、执行JavaScript,甚至处理AJAX请求,而无需打开实际的...

    传一个htmlunit依赖的jar包,官网所下

    HTMLUnit是一个Java库,它提供了一个无头浏览器的模拟,允许开发者在没有真实浏览器环境的情况下进行Web客户端测试和自动化。这个“htmlunit-2.25”压缩包包含的就是HTMLUnit库的版本2.25,是进行无头网页交互的关键...

    htmlunit-2.36和htmlunit-2.50,包括关联文件

    HTMLUnit是Java编程语言中的一款无头Web浏览器库,它模拟了浏览器的行为,但不涉及图形用户界面。这个工具主要用于自动化测试和网络爬虫,因为它能够解析HTML、执行JavaScript,并与网页上的各种元素进行交互。在...

    通过htmlunit获取执行js代码后的html文档

    通过HTMLUnit,开发者可以编写程序来模拟用户在浏览器上的操作,如点击链接、填写表单、执行JavaScript等,而无需真正打开一个浏览器实例。 使用HTMLUnit获取执行JavaScript后的HTML文档,首先需要理解HTMLUnit的...

    htmlunit-2.14

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化Web页面的测试和抓取。这个库的核心功能是能够解析、渲染和执行JavaScript,从而使得开发者可以在没有实际浏览器环境的情况下,对网页进行功能测试...

    htmlunit-2.1.5源码

    HTMLUnit是一个Java库,它模拟了一个无头Web浏览器,用于自动化网页测试和抓取。它提供了JavaScript支持,能够处理AJAX请求,使开发者能够在没有实际图形界面的情况下与网页交互。这个"htmlunit-2.1.5源码"包包含了...

    htmlunit-2.20.zip

    "htmlunit-2.20.zip"压缩包包含了HTMLUnit库的2.20版本,这个版本是针对Java JDK 1.7设计的,这意味着它与Java 7兼容。对于那些需要在旧项目中使用或者依赖Java 7环境的开发者来说,这是一个重要的考虑因素。值得...

    htmlunit-2.19-bin

    HTMLUnit是一个基于Java的无头Web客户端库,它模拟了一个完整的浏览器,但不涉及实际的图形用户界面。这个“htmlunit-2.19-bin”压缩包包含了HTMLUnit的二进制版本,允许开发者在Java应用程序中进行网页自动化测试和...

    Htmlunit2.8开发文档

    HtmlUnit 是一个强大的Java库,它模拟了一个无头Web浏览器,允许开发者进行自动化测试和网页抓取。在HtmlUnit 2.8版本中,你可以利用它来执行JavaScript、处理Ajax请求,以及与网页上的各种元素交互,而无需实际运行...

    最新版HtmlUnit2.22含API文档

    HtmlUnit是Java编程语言中的一款无头浏览器模拟库,它允许开发者在没有真实浏览器环境的情况下进行Web应用程序的测试和自动化。最新版HtmlUnit 2.22提供了对现代Web技术的强大支持,包括JavaScript、Ajax以及各种...

    htmlunit-2.33-API文档-中英对照版.zip

    赠送jar包:htmlunit-2.33.jar; 赠送原API文档:htmlunit-2.33-javadoc.jar; 赠送源代码:htmlunit-2.33-sources.jar; 赠送Maven依赖信息文件:htmlunit-2.33.pom; 包含翻译后的API文档:htmlunit-2.33-javadoc-...

    htmlunit-2.3..zip

    HTMLUnit是一款强大的无头Web客户端库,主要用于网页的单元测试。它模拟了一个浏览器,能够执行JavaScript,解析HTML,处理CSS,以及与网页上的各种元素进行交互。这个“htmlunit-2.3.zip”压缩包包含了运行和使用...

    htmlunit用到的jar包

    HTMLUnit是一个Java库,它提供了一个无头浏览器模拟器,用于自动化Web页面的测试和抓取。这个库允许开发者在不依赖于实际图形用户界面(GUI)的情况下与HTML页面进行交互,例如点击链接、填写表单和执行JavaScript。...

Global site tag (gtag.js) - Google Analytics