通过上述章节内容,我们得到了类别的数据,现在我们需要对每个类别进行商品的爬取。点击移动电源,进行商品总页数抓取,这个模块相对简单,正好适合用来练手。
我们可以从“列表页.png”的图片中看到,当前移动电源的页数为右上角所显示 1/100,即100页.
xpath的获取如第三张图所示,结果为
//*[@id="second-filter"]/div[2]/div/span
分析出了如果获取页数,我们现在要考虑的问题是,如果更新所有的类别。
其实思路非常简单,从数据库中取出对应的等级为3的类别(最底层类别),对这些类别进行循环,参数就是当前行的url,然后执行网页爬取代码,得到页数,更新数据。
根据Sql语句,得到等级为3的类别一共有4197个。这个时候就存在问题了,如果同步执行(循环一个一个执行),那么我的效率就很低,为了验证自己的写法。我以50个类别为例做了一个小demo测试性能。
//获取符合条件的列表
var urlList = _categoryService.GetListByLevel(3).Select(u => u.Url).ToList();
CategoryPageAnalysis.GetData(string url) 方法为获取类别个数方法
同步
循环执行,耗时18233毫秒
var dics = new Dictionary<string, int>();
foreach (var url in urlList)
{
dics.Add(url, CategoryPageAnalysis.GetData(url));
}
异步方法
6163毫秒 3倍的效率差
异步方法体的说明如下:
首先因为存在4197个类别,需要对这些类别进行分类。
4197/2000 约等于20. 即开20个线程,每个线程执行200条数据
int pageNum = 200;
int pageCount = urlList.Count % pageNum == 0 ? urlList.Count / pageNum : urlList.Count / pageNum + 1;
var pageListCollection = new List<List<string>>();
for (int i = 0; i < pageCount; i++)
{
var pageList = urlList.Skip(i * pageNum).Take(pageNum).ToList();
pageListCollection.Add(pageList);
}
Console.WriteLine(pageCount);
//异步 6163毫秒 3倍的效率差
int pageIndex = 1;
List<Task> taskList = new List<Task>();
foreach (var pageList in pageListCollection)
{
try
{
Task task = Task.Factory.StartNew(() =>
{
var dics = new Dictionary<string, int>();
foreach (var url in pageList)
{
dics.Add(url, CategoryPageAnalysis.GetData(url));
}
lock (lock_obj)
{
_categoryService.BatchUpdatePage(dics);
}
});
taskList.Add(task);
}
catch (Exception ex)
{
Console.WriteLine($"button3_Click 异步{ex.Message}");
}
}
存在的问题:
这种方法是为了单独解决这个问题而使用的,很笨拙,因为如果只有200个类别,多线程的意义就没有办法体现出来,这一点在之后的编码中我进行了修改。