作为"数据玩家",如果手头上没有数据怎么办?当然是用代码让程序自动化采集数据,但是现在"爬虫"不是那么容易,其中最困难的即是突破网站各种反爬机制。本系列将全面讲解 .NET 中一个非常成熟的库 —— selenium,并教会你如何使用它爬取网络上所需的数据
自动化爬虫虽然方便,但希望大家能顾及网站服务器的承受能力,不要高频率访问网站。并且千万不要采集敏感数据!!否则很容易"从入门到入狱"
本系列大部分案例同时采用 selenium 与 puppeteerSharp 库讲解,并且有 Python 和 C# 2门语言的实现文章,详细请到公众号目录中找到。
上一节入门案例中,我们知道等待机制是一个非常重要的功能,但是上一节中的代码,由于使用等待机制而变得太繁琐。
文章结构如下:
如果你只想方便使用,可以直接看最后一步关于如何使用即可。
来看看最终调用自己设计的语义调用代码的效果:
想象一下如果是一个机器人帮你从网页上查找某个信息,比较合理的流程是:
Selenium 的等待机制同样如此,而上述机制中唯一可以变化的就是"查找规则",这体现为 Wait.Until 的第一个参数接受一个"委托",每隔一段时间,就会执行你的方法。
这次项目自带 web 服务,启动调试会先启动 web 服务,在浏览器中输入本机 ip 即可浏览本文案例网页,操作看视频:
用"开发者工具",查看元素的标签:
现在用代码对这个页面采集。
导入命名空间
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
主要代码如下:
private static void FailFindAllContent()
{
using (var driver = new ChromeDriver())
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.IgnoreExceptionTypes(typeof(WebDriverException));
driver.Url = @"http://localhost:8081/";
var all = wait.Until(wd => wd.FindElements(By.ClassName("content")));
foreach (var item in all)
{
Console.WriteLine(item.Text);
}
Console.WriteLine("采集完毕!");
}
}
知道原因,那么我们很容易就能自己解决这个问题。 定义一个帮助方法:
private static Func<IWebDriver, IList<IWebElement>> UntilFindElements(string cssSelector)
{
IList<IWebElement> UntilFindElements_(IWebDriver driver)
{
var res = driver.FindElements(By.CssSelector(cssSelector));
if (res.Count > 0)
{
return res;
}
return null;
}
return UntilFindElements_;
}
调用很简单,原来的代码上,在 wait.Until 里面调用我们的帮助方法:
private static void TryFindAllContent()
{
using (var driver = new ChromeDriver())
{
………………
var all = wait.Until(UntilFindElements(".content"));
………………
}
}
如果每次使用 Selenium 都要写上这些代码,那真的太麻烦了。不过在 .net 中可以很容易扩展自己的语义。
现在从3个方面简化:
要做到以上的要求,其实很简单:
我已经简单制作了一个库,nuget安装即可:
Install-Package CrystalWind.SeleniumWrapper
使用如下:
using CrystalWind.SeleniumWrapper;
private static void GetBaiduSearch()
{
using (var Wrapper = WrapperBuilder.Create(new ChromeDriver()))
{
//进入搜索页面
Wrapper.Url = @"https://www.baidu.com/";
Wrapper.WaitForSendKeys("#kw", "爬虫");//在输入框中输入"爬虫"2字
Wrapper.WaitForClick("#su");//点击按钮
//找出所有主标题
var titles = Wrapper.WaitForFindElements("div#content_left h3>a");
//输出采集结果
foreach (var item in titles)
{
Console.WriteLine(item.Text);
}
Console.ReadKey();
}
}
这个库的源码就在本案例源码中,项目是"CrystalWind.SeleniumWrapper",内容非常简单。
用代码控制 selenium 最关键的功能就是"等待机制",我们可以用来检测各种条件,让代码无缝执行。