话不多说先上图

爬取10页大概500个帖子大概10s,500页2w多个帖子大概2min,由此可见性能并不是特别好,但是也没有很差。

好了话不多说,我们来一步一步实现这么个简易的客户端。

1.创建项目

创建一个WPF空项目,导入需要的Devexpress的dll

Devexpress可以到官网下载,基本16版本以上都可以。下载试用版的也可以,基本到期也不会限制你使用,只有开发的时候会弹出框,叉掉即可,比较良心。

下载地址:https://www.devexpress.com/

 2.编辑界面

基本就是xaml代码的编写,DevExpress的demo中心也有很多样例,直接上代码。

<dx:ThemedWindow x:Class="SearchAnyWay.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:local="clr-namespace:SearchAnyWay"
mc:Ignorable="d"
Title="百度贴吧搜索神器(v1.0)" Height="600" Width="800">
<Grid>
<dxlc:LayoutControl VerticalAlignment="Stretch" Orientation="Vertical" TextBlock.FontSize="11">
<Label VerticalAlignment="Top" FontWeight="Bold" Content="输入您需要查找的关键字"></Label>
<dxlc:LayoutGroup Orientation="Horizontal">
<dxlc:LayoutItem Label="关键字(K)" AddColonToLabel="True">
<dxe:TextEdit EditValue="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
<dxmvvm:Interaction.Triggers>
<dxmvvm:KeyToCommand KeyGesture="Enter" Command="{Binding SearchCommand}"></dxmvvm:KeyToCommand>
</dxmvvm:Interaction.Triggers>
</dxe:TextEdit>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="贴吧名(N)" AddColonToLabel="True">
<dxe:TextEdit EditValue="{Binding Path=HubName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</dxe:TextEdit>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="爬取页数(P)" AddColonToLabel="True">
<dxe:ComboBoxEdit ItemsSource="{Binding PageRange}"
SelectedItem="{Binding Page}"
ShowSizeGrip="False"
IsTextEditable="False">
</dxe:ComboBoxEdit>
</dxlc:LayoutItem>
<dxlc:LayoutGroup HorizontalAlignment="Right" VerticalAlignment="Center">
<dx:SimpleButton x:Name="btnSearch" Content="查找(S)" Width="80" Command="{Binding SearchCommand}"></dx:SimpleButton> </dxlc:LayoutGroup>
</dxlc:LayoutGroup>
<dxg:TreeListControl x:Name="treeList" Margin="0,10" ItemsSource="{Binding Source}"
SelectionMode="Row" SelectedItem="{Binding SelectedRow}">
<dxg:TreeListControl.Columns>
<dxg:TreeListColumn FieldName="Title" Header="标题" Width="2*"/>
<dxg:TreeListColumn FieldName="Brief" Width="2*" Header="详情"/>
<dxg:TreeListColumn Header="回复数" FieldName="CommentCount" Width="*"/>
<dxg:TreeListColumn Header="作者" FieldName="AuthorName" Width="*"/>
</dxg:TreeListControl.Columns>
<dxg:TreeListControl.View>
<dxg:TreeListView x:Name="view" VerticalScrollbarVisibility="Auto" AutoExpandAllNodes="True" AllowEditing="False" NavigationStyle="Row" ShowIndicator="False" TreeDerivationMode="ChildNodesSelector" ChildNodesPath="ICDItems">
<dxmvvm:Interaction.Triggers>
<dxmvvm:EventToCommand EventName="SourceUpdated" Command="{Binding Commands.ExpandAllNodes, ElementName=view}" />
<dxmvvm:EventToCommand EventName="RowDoubleClick" Command="{Binding SearchCommand}" CommandParameter="{Binding ElementName=treeList,Path=SelectedItem}" />
</dxmvvm:Interaction.Triggers>
</dxg:TreeListView>
</dxg:TreeListControl.View>
</dxg:TreeListControl>
<dxlc:LayoutGroup VerticalAlignment="Bottom" Orientation="Horizontal">
<Label Content="帖子总数:" HorizontalAlignment="Right"/>
<Label Content="{Binding Source.Count, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Right">
</Label>
</dxlc:LayoutGroup>
<dxlc:LayoutGroup VerticalAlignment="Bottom" Orientation="Horizontal">
<dxe:CheckEdit IsChecked="{Binding IsAll}" Content="Include All" HorizontalAlignment="Left"/>
<dx:SimpleButton Content="Copy VLPath To Clipboard" IsEnabled="{Binding CanNext}" Command="{Binding CopyVLPathCommand}" HorizontalAlignment="Left"></dx:SimpleButton>
<dxlc:LayoutGroup HorizontalAlignment="Right">
<dx:SimpleButton Content="下载(D)" Width="80" IsEnabled="{Binding CanNext}" Command="{Binding NextCommand}"></dx:SimpleButton>
<dx:SimpleButton Content="清除(C)" Width="80" IsEnabled="{Binding CanNext}" Command="{Binding OKCommand}"></dx:SimpleButton>
<dx:SimpleButton Content="合作(P)" Width="80" Command="{Binding CancelCommand}"></dx:SimpleButton>
</dxlc:LayoutGroup>
</dxlc:LayoutGroup>
</dxlc:LayoutControl>
<dx:WaitIndicator DeferedVisibility="{Binding IsLoading}" />
</Grid>
</dx:ThemedWindow>

3.实现mvvm模式。

这里采用了DevExpress自带的的mvvm模式,和WPF自带的去创建的框架基本一致。不了解mvvm的同学可以去园子里看看相关文章。

(1)后台代码设置主题还有绑定视图模型。

public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
//设置样式
ApplicationThemeHelper.UseLegacyDefaultTheme = true;
ApplicationThemeHelper.ApplicationThemeName = Theme.VisualStudioCategory;
this.WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
this.Icon = new BitmapImage(new Uri("../../debug.png",UriKind.Relative));
this.BorderThickness = new Thickness();
this.Margin = new Thickness();
this.Padding = new Thickness();
this.DataContext = new MainViewModel();
}
}

( 2 ) 设计帖子的实体类。

可以根据自己想要爬取的信息设计。

 public class ArticleModel
{
public string Title { get; set; }
public string Brief { get; set; }
public int CommentCount { get; set; }
public string AuthorName { get; set; }
}

(3)页数,帖子集合,等属性在ViewModel中进行声明。

//加载中
private bool _loading;
public bool IsLoading
{
get { return this._loading; }
set
{
SetProperty(ref _loading, value, () => IsLoading);
}
}
//贴吧名
private string _hub;
public string HubName
{
get { return this._hub; }
set
{
SetProperty(ref _hub, value, () => HubName);
}
}
//爬取页数
private int _page;
public int Page
{
get { return this._page; }
set
{
SetProperty(ref _page, value, () => Page);
}
}
//帖子集合
public ObservableCollection<ArticleModel> _source;
public ObservableCollection<ArticleModel> Source
{
get { return _source; }
set { SetProperty(ref _source, value, ()=>Source); }
}

(3)查询业务绑定到按钮的Command,下拉列表的绑定等。

public AsyncCommand SearchCommand { get; set; }

public IEnumerable<int> PageRange { get; private set; }
public MainViewModel()
{
Page = ;
PageRange = new List<int>() { ,, , , , , };
Source = new ObservableCollection<ArticleModel>();
SearchCommand = new AsyncCommand(Search);
}

4.爬虫业务的简单实现

我们使用HttpClient进行请求获取html页面的代码

使用AngleSharp解析html示例代码(按Ctrl+Shift+P快速安装NuGet包):Install-Package AngleSharp

相关简单使用:

//获取请求后response的页面代码。
string pageData = await http.GetStringAsync($"https://tieba.baidu.com/f?kw={HubName}&ie=utf-8&pn={pnIndex}");
//AngleSharp解析页面代码
IHtmlDocument doc = await parser.ParseDocumentAsync(pageData);

5.分析百度贴吧

可以看到URL基本一致,主要是一个URL参数会跟着页数而变化就是pn(Page Number),规律就是(Page-1)*50。50大概就是每页有50个帖子

那我们就好处理了,获取每个帖子的节点然后再去依次查找我们所需要的数据就可以了。

爬取的核心代码如下

await Task.Run(() =>
{
var http = new HttpClient();
var parser = new HtmlParser();
var result=Enumerable.Range(, Page)
.AsParallel()
.AsOrdered()
.SelectMany(page =>
{
return Task.Run(async () =>
{
var pnIndex = page * ;
//获取请求后response的页面代码。
string pageData = await http.GetStringAsync($"https://tieba.baidu.com/f?kw={HubName}&ie=utf-8&pn={pnIndex}".Dump());
//AngleSharp解析页面代码
IHtmlDocument doc = await parser.ParseDocumentAsync(pageData);
return doc.QuerySelectorAll(".t_con.cleafix").Select(tag => new ArticleModel()
{
Title = tag.QuerySelector(".j_th_tit").TextContent?.Trim(),
Brief= tag.QuerySelector(".threadlist_abs.threadlist_abs_onlyline")?.TextContent?.Trim(),
CommentCount=Convert.ToInt32(tag.QuerySelector(".threadlist_rep_num.center_text")?.TextContent),
AuthorName=tag.QuerySelector(".frs-author-name.j_user_card")?.TextContent?.Trim(),
}); ;
}).GetAwaiter().GetResult();
});
Source = new ObservableCollection<ArticleModel>(result);
});

一个小细节就是dom元素如果class中有空格查找的时候一定要用'.'来代替,比如dom元素class是'ftt poot'那么查找的时候就应该是tag.QuerySelector(".ftt.poot")坑里了我很久!!!可能是我这方面没怎么接触过吧。。。

好了,爬取的功能完成了,其他的边角料就自己随意发挥吧,哈哈。

代码下载地址:https://github.com/BruceQiu1996/WPF-/tree/master

c# WPF——完成一个简单的百度贴吧爬虫客户端的更多相关文章

  1. Prism for WPF 搭建一个简单的模块化开发框架 (一个节点)

    原文:Prism for WPF 搭建一个简单的模块化开发框架 (一个节点) 这里我就只贴图不贴代码了,看看这个节点之前的效果 觉得做的好的地方可以范之前的文章看看 有好的建议也可以说说   填充数据 ...

  2. Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单、导航

    原文:Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单.导航 这个实际上是在聊天之前做的,一起写了,也不分先后了 看一下效果图,上面是模块主导航,左侧是模块内菜单,现在加一下隐藏 ...

  3. Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务、WCF消息头添加安全验证Token

    原文:Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务.WCF消息头添加安全验证Token 为什么选择wcf?   因为好像wcf和wpf就是哥俩,,, 为什么选择异步 ...

  4. Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块

    原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...

  5. Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

    原文:Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单 昨天晚上把TreeView的样式做了一下,今天给TreeView绑了数据,实现了切换页面功能 上 ...

  6. Prism for WPF 搭建一个简单的模块化开发框架(二)

    原文:Prism for WPF 搭建一个简单的模块化开发框架(二) 今天又有时间了,再改改,加了一些控件全局的样式 样式代码 <ResourceDictionary xmlns="h ...

  7. Prism for WPF 搭建一个简单的模块化开发框架(一)

    原文:Prism for WPF 搭建一个简单的模块化开发框架(一) 最近闲来无事又想搞搞WPF..... 做个框架吧,可能又是半途而废....总是坚持不下来 不废话了, 先看一下工程结构 布局大概是 ...

  8. curl太复杂难用记不住?来试试Httpie一个简单的现代化命令行Http客户端

    HTTPie 是一个简单的现代化命令行 HTTP 客户端. 交互友好,JSON支持,语法高亮,类wget下载,支持拓展等 功能特性 自然而且简单的命令语句 格式化且高亮显示输出内容 内置 JSON 支 ...

  9. [Python]网络爬虫(六):一个简单的百度贴吧的小爬虫

    转自:http://blog.csdn.net/pleasecallmewhy/article/details/8927832 # -*- coding: utf-8 -*- #----------- ...

随机推荐

  1. Apache 的 php.ini 配置文件详解

    [root@taokey ~]# grep -v ";" /application/php/lib/php.ini [PHP] engine = On  ——→  是否启用 PHP ...

  2. H5微信授权登录

    这里介绍H5微信授权登录,采用了微信公众号授权原理,是oauth2的登录授权方式,简单的来讲,就是用户通过手机微信确认登录之后,微信方会返回一个授权码code给回第三方(接入方),这个授权码code一 ...

  3. php内置函数分析之array_fill_keys()

    PHP_FUNCTION(array_fill_keys) { zval *keys, *val, *entry; if (zend_parse_parameters(ZEND_NUM_ARGS(), ...

  4. php内置函数分析array_count_values()

    PHP_FUNCTION(array_count_values) { zval *input, /* Input array */ *entry, /* An entry in the input a ...

  5. HTML加载过程

    在地址栏输入url,返回html后,浏览器开始顺序加载并渲染DOM Body标签 当浏览器遇到body标签才算真正开始加载并渲染DOM,此时会有以下几种情况: DOM元素 浏览器遇到dom元素时,正常 ...

  6. 阿里云虚拟主机MYSQL加密长度16位变61位

    将网站迁移到阿里云虚拟主机后, 用户登录都提供密码错误, 一查询才发现MYSQL PASSWORD加密结果不致, 只有16位, 解决办法,在执行PASSWORD查询前,执行set old_passwo ...

  7. jq 获取各个元素的宽度高度的方法

    JS获取各种宽度.高度的简单介绍: scrollHeight: 获取对象的滚动高度. scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离 scrollTop:设置或获 ...

  8. IDEA将新建项目上传至GitLab

    1.首先,需要你自己登录GitLab,并新建一个项目的链接,如下图所示: (此图为图三,该链接下面操作中将会用到!) 2.在idea上新建一个项目,完成之后,需要创建一个git仓库: 3.然后可以根据 ...

  9. 跳转控制语句return

    return语句的作用不是为了跳出循环,更常用的功能是结束一个方法,也就是退出一个方法,跳转到上层调用的方法处. 演示案例: 结束循环其实是结束了main方法 public static void m ...

  10. 【CF1247F】Tree Factory(构造)

    题意:给定一棵n个点的树,要求将一条可以随意标号的链通过若干次操作变成这棵树 一次操作是指若v不为根且v的父亲不为根,则将v以及v的子树移到v的父亲的父亲上 要求给出标号方案,操作次数以及方案 n&l ...