https://www.selenium.dev/documentation/

1 Selenium简介

Selenium最初是由Shinya Kasatani基于火狐(Firefox)浏览器开发的工具,其主要用于网站的自动化测试。读者可以在火狐浏览器中安装Selenium IDE插件,并使用该插件录制在浏览器中的执行动作(如表单提交、单击和鼠标的移动等)。在本章中,我们将重点介绍Selenium WebDriver的使用。Selenium WebDriver主要应用于程序(Java、Python和C#等)与浏览器的交互,其可以用来实现数据的采集。

Selenium不自带浏览器,需要与第三方浏览器结合使用,如本章与Selenium结合使用的浏览器为火狐浏览器(版本号为56.064位)。相比Jsoup和HttpClient等工具,Selenium有其特有优势,如自动加载页面(执行JavaScript脚本)、模拟真实的浏览器操作(可用于模拟登录)等。

2 JavaSelenium环境搭建

首先,创建一个Java Maven工程,并在该工程下添加两个文件夹drivers和libs,
如图8.1所示。
image.png
图8.1 Java Maven工程的项目结构

其次,在Java中使用Selenium,需要下载相关jar包,在相关界面(见图8.2)
下载selenium-server-standalone-3.141.59.jar。下载完成后,将该jar包放到工程的libs目
录下,并将该jar包引入项目。另外一种配置相关jar包的方式是单击图8.3中所示的
“Download”按钮,下载selenium-java-3.141.59压缩包,并进行解压,之后将解压后
文件夹中的所有jar包放到libs目录下,并将这些jar包引入项目。再者,也可以直接
使用Maven工程中的pom.xml文件配置所需的jar包。

  1. <dependency>
  2. <groupId>org.seleniumhq.selenium</groupId>
  3. <artifactId>selenium-java</artifactId>
  4. <version>3.141.59</version>
  5. </dependency>

image.png

在相关jar包配置完成之后,需要下载Selenium WebDriver连接火狐浏览器的工
具geckodriver。
此处使用的火狐浏览器的版本号为56,因此这里下载geckodriver-v0.18.0-win64.zip,
如图8.4所示。下载完成后,解压该压缩文件,并将文件夹中的geckodriver.exe放置
到项目的drivers目录下。
image.png

3 浏览器的操控

搭建好Java Selenium环境后,便可以利用Java程序操控火狐浏览器,获取网页的响应内容。下面以某搜索网站为例,主要任务是使用Selenium WebDriver和geckodriver启动火狐浏览器,打开某搜索网站首页,并在搜索框中自动输入“Java网络爬虫”,执行搜索,最后针对响应页面使用Xpath语法解析得到搜索结果的标题
和URL。
程序8-1为该案例的详细代码。这段代码中使用setProperty(String key,String value)配置geckodriver,然后实例化FirefoxDriver(声明使用的是火狐浏览器),接着使用火狐浏览器打开页面执行一系列操作。值得注意的是在执行搜索的过程中,需要利用implicitly Wait)方法休息一定的时间,以供网页完全加载数据。最后,使用Xpath语法进行元素定位,解析相应的字段。
程序8-1

  1. package com.qian.test;
  2. import java.util.List;
  3. import java.util.concurrent.TimeUnit;
  4. import org.openqa.selenium.By;
  5. import org.openqa.selenium.WebElement;
  6. import org.openqa.selenium.firefox.FirefoxDriver;
  7. public class Test {
  8. public static void main(String[] args) {
  9. //geckodriver配置
  10. System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
  11. //声明使用的是火狐浏览器
  12. FirefoxDriver driver = new FirefoxDriver();
  13. //使用火狐浏览器打开百度
  14. driver.get("http://www.baidu.com");
  15. //元素定位
  16. driver.findElement(By.id("kw")).sendKeys("Java 网络爬虫");
  17. driver.findElementById("kw").sendKeys("Java 网络爬虫");
  18. driver.findElementById("su").click();
  19. //给出一定的响应时间
  20. driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
  21. driver.getPageSource();
  22. //使用xpath解析数据
  23. List<WebElement> titleList =
  24. driver.findElements(By.xpath("//*[@class='result c-container ']/h3/a"));
  25. //输出新闻标题
  26. for(WebElement e : titleList){
  27. System.out.println("标题为:" + e.getText() + "\t" + "url为:"
  28. + e.getAttribute("href"));
  29. }
  30. driver.quit(); // 关闭浏览器
  31. }
  32. }

执行程序会发现,相关操作都会在浏览器中自动执行。另外,在控制台中也会输出解析的标题和URL,如果8.5所示。
image.png

4 元素定位

在使用Selenium时,往往需要先通过定位器找到相应的元素,然后再进行其他操作。例如,在程序8-1中,先使用findElementById(“kw”)定位到搜索框,随后再利用sendKeys(CharSequence.keysToSend)方法输入搜索内容。定位器是一种抽象查询语言,功能是定位元素。Selenium WebDriver提供了多种定位策略,如id定位、name定位、class定位、tag name定位、link text定位、Xpath定位和CSS定位等。下面将分别介
绍这些定位策略。

4.1 id定位

id定位,即通过id在网页中查询元素,使用简单且常用。例如,我们想要通过id定位百度首页的搜索框。首先,在谷歌浏览器中打开该页面,在搜索框的位置右击“检查”,便会看到图8.6所示的内容,进而可以确定搜索框可以通过id=‘kw’进行定位。同理,我们可以通过id=‘su’定位开始搜索按钮,如图8.7所示。
image.png
image.png

在Java中,有以下四种方式实现id定位策略,如程序8-2所示。
程序8-2
//第一种方式返回WebElement
driver.findElementById()
//第二种方式返回List
driver.findElementsById()
//第三种方式返回WebE1 ement
driver.findElement (By.id())
//第四种方式返回List
driver.findElements (By.id())

4.2 name定位

当元素中包含name属性时,可以使用name进行定位。如图8.6中的搜索框也可以使用name=‘wd’定位。在Java中,有以下四种方式实现name定位策略,如程序8-3所示。

程序8-3
//第一种方式返回WebE1 ement
driver.findElementByName ()
//第二种方式返回List
driver.findElementsByName ()
//第三种方式返回WebE1 ement
driver.findElement (By.Name ())
//第四种方式返回List
driver.findElements (By.Name ())

4.3 class定位

当元素中包含class属性时,可以使用class进行定位。如图8.7中的按钮也可以
使用class=‘btn self-btn bg s btn’定位。在Java中,有以下四种方式实现class定位
策略,如程序8-4所示。程序8-4
//第一种方式返回WebE1 ement
driver.findElementByClassName ()
//第二种方式返回List
driver.findElementsByClassName ()
//第三种方式返回WebElement
driver.findElement (By.ClassName ())
//第四种方式返回List
driver.findElements (By.ClassName ())

4.4 tagname定位

Selenium WebDriver也提供了tag name(标签名称)定位的方法,使用标签名称可以很方便地定位一些元素,如定位所有表格中的、定位所有链接等。程序8-5展示了实现tag name定位策略的四种方式。
程序8-5
//第一种方式返回WebElement
driver.findElementByTagName ()
//第二种方式返回List
driver.findElementsByTagName ()
//第三种方式返回WebE1 ement
driver.findElement (By.tagName ())
//第四种方式返回List
driver.findElements (By.tagName ())

4.5 linktext定位

link text定位,是通过链接文本定位链接的。在Java中,有以下四种方式实现lnk
text定位策略,如程序8-6所示。
程序8-6
//第一种方式返回WebE1 ement
driver.findElementByLinkText()
//第二种方式返回List
driver.findElementsByLinkText ()
//第三种方式返回WebElement
driver.findElement (By.linkText ())
//第四种方式返回List
driver.findElements (By.linkText ())

4.6 Xpath定位

Selenium WebDriver支持使用Xpath表达式定位元素,如程序8-1中使用Xpath表达式定位一些元素。Xpath使用路径表达式来定位HTML或XML文档中的节点或节点集合,在5.1.2节中已详细介绍了Xpath语法,这里不再赘述。在Java中,同样可以使用四种方式实现Xpath定位策略,如程序8-7所示。
程序8-7
//第一种方式返回WebE1 ement
driver.findElementByXPath ()
//第二种方式返回List
driver.findElementByXPath ()
//第三种方式返回WebE1 ement
driver.findElement (By.xpath ())
//第四种方式返回List
driver.findElements(By.xpath())

4.7 CSS选择器定位

在5.1.1节中,介绍了CSS选择器,这里不再赘述。Selenium WebDriver在定位
元素时,也可以使用CSS选择器,其实现方式有四种,如程序8-8所示。
程序8-8
//第一种方式返回WebE1 ement
driver.findElementByCssSelector ()
//第二种方式返回List
driver.findElementsByCssSelector ()
//第三种方式返回WebElement
driver.findElement (By.cssSelector ())
//第四种方式返回List
driver.findElements (By.cssSelector ())

5 模拟登录

Selenium可以精准地定位浏览器中的元素(如输入框)模拟真实的浏览器操作(如输入文本、单击等)。针对一些复杂且需要登录才能获取数据的网站,可以利用Selenium的特性,模拟登录该网站,获取登录的Cookie。之后,使用Jsoup或者Httpclient采集该网站中的数据。以下,将以某网站的模拟登录为例,介绍Selenium的使用情况。
首先,使用谷歌或火狐浏览器,打开该网站登录页面。之后,在用户名、密码和登录按钮的位置分别右击“检查”,获取这三个元素的定位信息。由图8.8可知,可以使用name=‘email’定位用户名框。同理,可以通过id=‘password’定位密码框,通过id=‘login’定位登录按钮。
image.png

利用Selenium,输入用户名和密码,并单击“登录”按钮执行登录操作。在登录完成后,使用Jsoup请求个人页面的数据,如图8.9所示。
image.png

程序8-9给出了为模拟登录的完整代码。需要注意的是在模拟单击登录按钮后,
需要使用sleep((long millis)方法休息一段时间,使登录信息加载完整。在Jsoup请求指定页面时,使用了登录后的Cookie信息。执行程序8-9,会发现相关登录操作会在浏览器中自动执行。同时,控制台也会输出解析得到的内容,如图8.10所示。

程序8-9

  1. package com.qian.test;
  2. import java.io.IOException;
  3. import java.util.Set;
  4. import java.util.concurrent.TimeUnit;
  5. import org.jsoup.Connection.Response;
  6. import org.jsoup.Jsoup;
  7. import org.jsoup.nodes.Document;
  8. import org.jsoup.nodes.Element;
  9. import org.openqa.selenium.Cookie;
  10. import org.openqa.selenium.firefox.FirefoxDriver;
  11. public class LoginRenren {
  12. public static void main(String[] args) throws IOException, InterruptedException {
  13. //geckodriver配置
  14. System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
  15. //声明使用的是火狐浏览器
  16. FirefoxDriver driver = new FirefoxDriver();
  17. //使用火狐浏览器打开人人网
  18. driver.get("http://sns.******.com/");
  19. //元素定位,提交用户名以及密码
  20. //清空后输入
  21. driver.findElementByName("email").clear();
  22. driver.findElementByName("email").sendKeys("*********");
  23. driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
  24. //清空后输入
  25. driver.findElementById("password").clear();
  26. driver.findElementById("password").sendKeys("*******");
  27. //元素定位,点击登陆按钮
  28. driver.findElementById("login").click();
  29. //休息一段时间,使得网页充分加载。注意这里非常有必要
  30. Thread.sleep(10 * 1000);
  31. Set<Cookie> cookies = driver.manage().getCookies();
  32. //获取登陆的cookies
  33. String cookieStr = "";
  34. for (Cookie cookie : cookies) {
  35. cookieStr += cookie.getName() + "=" + cookie.getValue() + "; ";
  36. }
  37. System.out.println(cookieStr);
  38. //基于Jsoup,使用cookies请求个人信息页面
  39. //添加一些header信息
  40. Response orderResp = Jsoup
  41. .connect("http://www.******.com/427727657/profile?v=info_timeline")
  42. .header("Host", "www.******.com")
  43. .header("Connection", "keep-alive")
  44. .header("Cache-Control", "max-age=0")
  45. .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*;q=0.8")
  46. .header("Origin", "http://www.******.com")
  47. .header("Referer", "http://www.******.com/SysHome.do")
  48. .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0")
  49. .header("Content-Type", "application/x-www-form-urlencoded")
  50. .header("Accept-Encoding", "gzip, deflate, br")
  51. .header("Upgrade-Insecure-Requests", "1")
  52. .cookie("Cookie", cookieStr)
  53. .execute();
  54. //解析数据
  55. Document doc = orderResp.parse();
  56. //System.out.println(doc);
  57. org.jsoup.select.Elements elements = doc.select("div[class=info-section-info]")
  58. .select("dl[class=info]");
  59. for (Element element : elements) {
  60. if (element.select("dt").text().contains("大学")) {
  61. System.out.println(element.text());
  62. }
  63. }
  64. // 关闭浏览器
  65. driver.quit();
  66. }
  67. }

image.png

6 动态加载JavaScript数据(操作滚动条)

在使用Jsoup和Httpclient直接请求URL时,有时会发现响应得到的HTML中包含的信息不全,未展示出的信息必须通过执行页面中的JavaScript代码才能展示。相比于Jsoup和Httpclient,Selenium可以利用JavascriptExecutor接口执行任意JavaScript代码。下面将以国外某网站中的新闻数据采集为例,讲解如何使用Selenium动态加载JavaScript数据。
在进入新闻的某一网页时,下拉滚动条,会发现底部展示了一系列的相关新闻(网站推荐的内容),如图8.11所示,继续下拉滚动条,新的新闻内容会不断地加载出来。
image.png

为了采集每条新闻对应的一系列相关新闻,则需要不断地向下加载数据,即下拉浏览器滚动条。程序8-10给出了动态加载JavaScript数据的代码。在程序8-10中,使用JavascriptExecutor接口的executeScript(String script,Object..args)方法执行JavaScript代码,具体的JavaScrript代码为scrollTo方法,即页面滚动方法。在执行第一次滚动时,都需要休息一定的时间,以使得网页充分加载数据。在滚动完成之后,获取该网页的HTML内容并使用Jsoup工具解析相关新闻数据。执行程序8-10,会在控制台输出新闻的标题和URL,如图8.12所示。

  1. package com.qian.test;
  2. import java.io.IOException;
  3. import org.jsoup.Jsoup;
  4. import org.jsoup.nodes.Document;
  5. import org.jsoup.nodes.Element;
  6. import org.jsoup.select.Elements;
  7. import org.openqa.selenium.JavascriptExecutor;
  8. import org.openqa.selenium.firefox.FirefoxDriver;
  9. public class FinancialNewsRolling {
  10. public static void main(String[] args) throws IOException, InterruptedException {
  11. //geckodriver配置
  12. System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
  13. //声明使用的是火狐浏览器
  14. FirefoxDriver driver = new FirefoxDriver();
  15. //使用火狐浏览器打开任意某一新闻页
  16. driver.get("https://www.financialexpress.com/industry" + "/nclat-rejects-hdfc-plea-for-insolvency-" + "proceedings-against-rhc-holding/1640159/");
  17. // 执行JS操作
  18. JavascriptExecutor JS = (JavascriptExecutor) driver;
  19. Thread.sleep(3000);
  20. try {
  21. JS.executeScript("scrollTo(0, 5000)");
  22. System.out.println("1");
  23. //调整休眠时间可以获取更多的内容
  24. Thread.sleep(5000);
  25. JS.executeScript("scrollTo(5000, 10000)");
  26. System.out.println("2");
  27. Thread.sleep(5000);
  28. // 继续下拉
  29. JS.executeScript("scrollTo(10000, 30000)");
  30. System.out.println("3");
  31. Thread.sleep(5000);
  32. //继续下拉
  33. JS.executeScript("scrollTo(10000, 50000)");
  34. System.out.println("4");
  35. } catch (Exception e) {
  36. System.out.println("Error at loading the page ...");
  37. driver.quit();
  38. }
  39. String html = driver.getPageSource();
  40. //System.out.println(html);
  41. //解析数据
  42. Document doc = Jsoup.parse(html);
  43. Elements elements = doc.select("[id=taboola-below-article]").select("div[id~=taboola-below-article-p?]");
  44. for (Element ele : elements) {
  45. String newsTitle = ele.select("a[class= item-label-href]").attr("title");
  46. String newsUrl = ele.select("a[class= item-label-href]").attr("href");
  47. System.out.println(newsTitle + "\t" + newsUrl);
  48. }
  49. // 关闭浏览器
  50. driver.quit();
  51. }
  52. }

image.png

7 隐藏浏览器

由以上内容可知使用Selenium采集网磁数据时,需要不断地调用浏览器。实际上,通过对Selenium的设置,可以达到隐藏浏览器的效果。仍以8.5节中的新联页面为例,程序8-11中给出了采集该新闻页面标题的代码。在程序8-11中,对火狐浏览器设置了headless,其作用是实现无界面状态。同时,这里使用while循环的方式,以防止加载浏览器请求页面失败。最后,程序执行JavaScript代码,获取新闻的标题,执行程序8-11,会在控制台输出新闻的标题信息。

程序8-11

  1. package com.qian.test;
  2. import java.io.IOException;
  3. import java.util.concurrent.TimeUnit;
  4. import org.openqa.selenium.JavascriptExecutor;
  5. import org.openqa.selenium.firefox.FirefoxBinary;
  6. import org.openqa.selenium.firefox.FirefoxDriver;
  7. import org.openqa.selenium.firefox.FirefoxOptions;
  8. public class JavaScriptTest {
  9. public static void main(String[] args) throws IOException, InterruptedException {
  10. FirefoxBinary firefoxBinary = new FirefoxBinary();
  11. firefoxBinary.addCommandLineOptions("--headless");
  12. //设置路径
  13. System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
  14. FirefoxOptions firefoxOptions = new FirefoxOptions();
  15. firefoxOptions.setBinary(firefoxBinary);
  16. FirefoxDriver driver = new FirefoxDriver(firefoxOptions);
  17. //直到加载该网页为止
  18. while (true) {
  19. try {
  20. driver.get("https://www.financialexpress.com" + "/industry/nclat-rejects-hdfc-plea" + "-for-insolvency-proceedings-against" + "-rhc-holding/1640159/");
  21. } catch (Exception e) {
  22. driver.quit();
  23. driver = new FirefoxDriver(firefoxOptions);
  24. driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
  25. continue;
  26. }
  27. break;
  28. }
  29. //滚动条操作
  30. JavascriptExecutor JS = (JavascriptExecutor) driver;
  31. // 执行JS操作,返回新闻的标题
  32. String title = (String) JS.executeScript("return document.title");
  33. System.out.println(title);
  34. // 关闭浏览器
  35. driver.quit();
  36. }
  37. }

8 截取验证码

在网络爬虫中,很多网站会采用验证码的方式来反爬虫,例如在登录时设置验证码、频繁访问时自动弹出验证码等。针对模拟登录时的验证码输入问题,一种简单的解决方案是将验证码保存到本地,之后在程序中手动输入即可。但对于采集每页都需要验证码的网站来说,则需要使用验证码识别算法或调用一些OCR API来自动识别验证码,以保证其效率。
image.png
使用Jsoup或者Httpclient,可以直接将验证码下载到本地。而对Selenium来说,可以使用截图的方式截取验证码,并保存到本地。下面,以某搜索网站为例介绍Selenium如何实现截取验证码,在程序中手动输入验证码内容,实现数据的采集。图8.13展示了需要输入的关键词及关键词对应的部分文章数据。

在浏览器中打开网页时,会出现图8.14所示的情况,即访问异常,需要输入验证码。为此,我们使用Selenium截取验证码。首先,利用浏览器检查元素,使用id=‘seccodelmage’定位到验证码,接着使用getScreenshotAs以及getSubimage方法截取该验证码。程序8-12给出了完整的数据采集代码。在手动输入验证码之后,单击“提
交”按钮,便能看到所需内容。
image.png

执行程序8-12,会在“E:/钱洋个人/IdentifyingCode/”目录下出现test.png图片文件,如图8.15所示。在控制台输入验证码内容“8628bs”,便可以成功获取关键词对应的文章数据,如图8.16所示。

程序8-12

  1. package com.qian.test;
  2. import java.awt.image.BufferedImage;
  3. import java.io.BufferedReader;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.io.InputStreamReader;
  7. import java.util.concurrent.TimeUnit;
  8. import javax.imageio.ImageIO;
  9. import org.jsoup.Jsoup;
  10. import org.jsoup.nodes.Document;
  11. import org.jsoup.nodes.Element;
  12. import org.jsoup.select.Elements;
  13. import org.openqa.selenium.By;
  14. import org.openqa.selenium.OutputType;
  15. import org.openqa.selenium.Point;
  16. import org.openqa.selenium.TakesScreenshot;
  17. import org.openqa.selenium.WebElement;
  18. import org.openqa.selenium.firefox.FirefoxBinary;
  19. import org.openqa.selenium.firefox.FirefoxDriver;
  20. import org.openqa.selenium.firefox.FirefoxOptions;
  21. public class ScreenshotTest {
  22. public static void main(String[] args) throws IOException, InterruptedException {
  23. FirefoxBinary firefoxBinary = new FirefoxBinary();
  24. firefoxBinary.addCommandLineOptions("--headless");
  25. //设置路径
  26. System.setProperty("webdriver.gecko.driver", "drivers\\geckodriver.exe");
  27. FirefoxOptions firefoxOptions = new FirefoxOptions();
  28. firefoxOptions.setBinary(firefoxBinary);
  29. FirefoxDriver driver = new FirefoxDriver(firefoxOptions);
  30. //直到加载该网页为止
  31. while (true) {
  32. try {
  33. driver.get("http://weixin.******.com/antispider/?"
  34. + "from=%2fweixin%3Ftype%3d2%26query"
  35. + "%3dcomputer+%26ie%3dutf8%26s_from%"
  36. + "3dinput%26_sug_%3dy%26_sug_type_%3d");
  37. } catch (Exception e) {
  38. driver.quit();
  39. driver = new FirefoxDriver(firefoxOptions);
  40. driver.manage().timeouts()
  41. .pageLoadTimeout(10, TimeUnit.SECONDS);
  42. continue;
  43. }
  44. break;
  45. }
  46. WebElement webEle = driver.findElement(By.id("seccodeImage"));
  47. // Get entire page screenshot
  48. java.io.File screenshot = ((TakesScreenshot) driver)
  49. .getScreenshotAs(OutputType.FILE);
  50. BufferedImage fullImg = ImageIO.read(screenshot);
  51. Point point = webEle.getLocation();
  52. int eleWidth = webEle.getSize().getWidth();
  53. int eleHeight = webEle.getSize().getHeight();
  54. BufferedImage eleScreenshot = fullImg.getSubimage(point.getX(), point.getY(),
  55. eleWidth, eleHeight);
  56. ImageIO.write(eleScreenshot, "png", new File("E:/钱洋个人/IdentifyingCode/test.png"));
  57. System.out.println("请输入验证码:");
  58. BufferedReader buff = new BufferedReader(new InputStreamReader(System.in));
  59. String captcha_solution = "";
  60. try {
  61. captcha_solution = buff.readLine();
  62. } catch (IOException e) {
  63. e.printStackTrace();
  64. }
  65. driver.findElement(By.name("c")).sendKeys(captcha_solution);
  66. driver.findElementById("submit").click();
  67. //休息一段时间,使得网页充分加载。注意这里非常有必要
  68. Thread.sleep(10 * 1000);
  69. String html = driver.getPageSource();
  70. Document doc = Jsoup.parse(html);
  71. Elements elements = doc.select("div[class=txt-box]");
  72. for (Element ele : elements) {
  73. String newsTitle = ele.select("h3").select("a").text();
  74. String newsUrl = ele.select("h3").select("a").attr("href");
  75. System.out.println(newsTitle + "\t" + newsUrl);
  76. }
  77. // 关闭浏览器
  78. driver.quit();
  79. }
  80. }

image.png

9 本章小结

本章主要介绍了Selenium WebDriver在网络爬虫中的应用。相比Jsoup、Httpclient
和URLConnection,Selenium的主要优势是可以与浏览器进行交互(如输入文字、单
击按钮等)及执行加载JavaScript代码。但Selenium也存在缺点,如每执行一个URL
都相当于在浏览器中打开一个网页,并且需要加载网页中的JavaScript代码,因此,
Selenium的效率较低,仅适用于小规模数据采集。