今天研究了下网络爬虫,有不少这方面的文章,开始找到的是用HttpRequest进行抓取,但是这种抓取对某些网站显然是不行的。比如知乎,要抓取信息必须先登录。又搜索这方面的内容,网上信息繁杂且混乱,而且关于C#方面的内容十分的少。
在研究了很久,尝试了更久之后,终于初步实现了这一功能,代码位置:https://codechina.csdn.net/wjwlsyd/netcrawler/-/commits/master
我用的是VS2019,有兴趣的可以下载下来看一下。
做的时候遇到一个坑,Selenium的ChromeDriver版本必须和本地的Chrome版本保持一致,否则会报错。但是似乎本地的Chrome版本一直是自动更新的,难道每次都得改?这一点我没做深入研究,如果你们遇到这样的问题可以去网上搜索一下,网上关于这方面的内容很多都是Python的,不过各位仔细找一下应该也可以找到,我就不在这里赘述了。
1.创建一个控制台程序,然后把Selenium相关的包从Nuget上下载安装上。
2.处理登录逻辑
处理登录的步骤大概是这样:
1)首先使用Selenium启动浏览器并跳转至目标界面(知乎)
2)知乎会跳转至登录界面,这时,在程序里Sleep一段时间,或者打个断点。当程序运行至断点时,我们则手工去知乎登录界面登录上,等到跳转后,再继续断点。断点之后的代码中处理知乎响应的Cookie并将Cookie保存至本地的Json文件中
3)为了防止第二次登录时仍然需要手工处理,我们可以在这里加上文件存在判断,如果文件存在了就不需要再次保存了,并且直接从Json文件中读取Cookie的值附加到Selenium中。附加好Cookie后再次访问知乎界面,这时候它就直接登录进去了。关闭程序再次运行也不需要手工登录了。
4)再使用Selenium抓取数据就可以了
上面的第二步和第三步也许可以不需要,我没有尝试,但是我觉得是可以的。也就是如果你只想抓下数据的话,我觉得可以直接把断点打在登录前,等手工登录后,再继续断点,断点后直接抓取数据。感兴趣的可以自己尝试下。
贴上代码:
保存Cookie处理登录:
public void BootLogin(WebDriverAction Action)
{
Action.Bootbrowser();
Thread.Sleep(2000);//login in web manually
if (!GlobalHelper.CheckJsonFileExists("cookies"))
{
var cookies = Action.Driver.Manage().Cookies;
var str = JsonConvert.SerializeObject(cookies);
GlobalHelper.WritetoJsonFile(str, "cookies");
}
dynamic cookieObj = "cookies.json".ReadJsonFile<ExpandoObject>();
foreach (var cookieO in cookieObj.AllCookies)
{
DateTime? expiry = null;
try
{
var exp = (long)cookieO.expiry;
expiry = exp.ToDate();
}
catch
{
}
Cookie cookie = new Cookie(cookieO.name, cookieO.value, cookieO.domain, cookieO.path, expiry);
Action.Driver.Manage().Cookies.AddCookie(cookie);
}
//redirect without login
Action.GotoUrl(GlobalConstants.BaseUrl);
}
抓取数据:
public void SearchArticles(WebDriverAction Action)
{
var results = new List<Tuple<string, string>>();
for (var i = 0; i <= 20; i++)
{
var elements = Action.Driver.FindElements(By.XPath("//div[@class='ContentItem AnswerItem']"));
foreach (var e in elements)
{
var attr = e.GetAttribute("data-zop");
dynamic jo = JsonConvert.DeserializeObject<ExpandoObject>(attr);
var a = new Tuple<string, string>(jo.authorName, jo.title);
results.Add(a);
}
Action.Scroll();
Thread.Sleep(500);
}
var rstr = JsonConvert.SerializeObject(results);
GlobalHelper.WritetoJsonFile(rstr, "results" + DateTime.Now.ToString("yyyyMMddHHmmss"));
}
Program:
class Program
{
static void Main(string[] args)
{
WebAction action = new WebAction();
WebDriverAction driverAction = new WebDriverAction();
try
{
action.BootLogin(driverAction);
action.SearchArticles(driverAction);
}
catch(Exception ex)
{
throw ex;
}
finally
{
driverAction.Closebrowser();
}
Console.ReadKey();
}
}