详解C#如何利用爬虫技术实现快捷租房
作者:Csharp小记 发布时间:2021-11-02 21:49:38
标签:C#,爬虫,租房
场景
做为一个码农,大部分都集中在一二线城市,所以租房也就无可避免,面对如今五花八门的租房信息,往往很难找到合适的房子。而如今的这些租房软件,大部分也都被中介、广告等给占据了。除了去中介公司,感觉再也找不到合适的房子,但是面对50%的中介费,很大程度上也难以忍受。偶然之间听朋友说到可以去豆瓣看看,然后我就试着去寻找一下,结果发现豆瓣讨论组的信息太过杂乱,先不说房源的好坏,光是找合适位置的帖子就得翻好久。。。
需求
所以,综上场景所述,就写了个简单的爬虫来爬取需要的位置以及最新发布的一些房源帖子。这里实现比较简单,同样豆瓣也有做一些防爬处理(IP访问次数等),而我并不是为了获取更多的信息而爬取,只是为了方便。所以既没有多线程处理,也没有做反防爬。每次获取的信息也足够看了,不行的话就等第二天再看呗。
开发环境
.NET Framework版本:4.5
开发工具
Visual Studio 2013
实现代码
private readonly string douBanUrl = "https://www.douban.com/group/search?cat=1019&sort=time";
bool isStop = false;
public FormCrawler()
{
InitializeComponent();
}
private void btn_start_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txt_city.Text))
{
MessageBox.Show("请输入城市!");
return;
}
if (string.IsNullOrWhiteSpace(txt_keys.Text))
{
MessageBox.Show("请输入关键字!");
return;
}
//初始化控件
btn_start.Enabled = false;
btn_stop.Enabled = true;
listView1.Items.Clear();
progressBar1.Value = 0;
List<string> groups = GetGroupUrls();
progressBar1.Maximum = groups.Count;
Task.Run(() =>
{
GetHouseInfo(groups);
this.BeginInvoke(new Action(() =>
{
btn_start.Enabled = true;
btn_stop.Enabled = false;
}));
});
}
private void btn_stop_Click(object sender, EventArgs e)
{
isStop = true;
btn_start.Enabled = true;
btn_stop.Enabled = false;
progressBar1.Value = progressBar1.Maximum;
}
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
ListView.SelectedListViewItemCollection selectItem = listView1.SelectedItems;
if (selectItem.Count > 0)
{
Process.Start(selectItem[0].SubItems[1].Text);
}
}
/// <summary>
/// 获取前100个讨论组
/// </summary>
/// <returns></returns>
private List<string> GetGroupUrls()
{
List<string> groupUrls = new List<string>();
int pageSize = 20;//豆瓣每页显示20个讨论组
string groupUrl = string.Empty;
for (int groupNum = 0; groupNum <= 80; groupNum += pageSize)
{
if (isStop) break;
groupUrl = string.Format(douBanUrl + "&q={0}&start={1}", HttpUtility.UrlEncode(txt_city.Text + "租房", Encoding.UTF8), groupNum);
string text = HttpUtil.HttpGet(groupUrl);
if (!string.IsNullOrWhiteSpace(text))
{
Regex reg = new Regex(@"(?is)(?<=<div\sclass=""pic""[^>]*?>).*?(?=</div>)");
MatchCollection matchs = reg.Matches(text);
for (int i = 0; i < matchs.Count; i++)
{
if (matchs[i].Success)
{
Regex regHref = new Regex("(?<=href\\s*=\\s*\")\\S+(?<!\")");
string href = regHref.Match(matchs[i].Value).Value;
groupUrls.Add(href);
}
}
}
}
return groupUrls;
}
/// <summary>
/// 获取讨论组内符合条件的房源
/// </summary>
/// <param name="urls"></param>
private void GetHouseInfo(List<string> urls)
{
for (int u = 0; u < urls.Count; u++)
{
if (isStop) break;
this.BeginInvoke(new Action(() =>
{
progressBar1.Value++;
}));
string text = HttpUtil.HttpGet(urls[u] + "/discussion?start=");
Regex regex = new Regex(@"(?is)(?<=<tr\sclass=""""[^>]*?>).*?(?=</tr>)");
MatchCollection matchs = regex.Matches(text);
for (int i = 0; i < matchs.Count; i++)
{
if (matchs[i].Success)
{
#region 匹配时间
Regex regDate = new Regex(@"(?is)(?<=<td\snowrap=""nowrap""\sclass=""time""[^>]*?>).*?(?=</td>)");
Match matchDate = regDate.Match(matchs[i].Value);
if (!matchDate.Success)
{
continue;
}
string time = matchDate.Value;
DateTime dtTemp = new DateTime();
if (!DateTime.TryParse(time, out dtTemp))
{
time = time.Insert(0, DateTime.Now.Year + "-");
dtTemp = Convert.ToDateTime(time);
}
if (dtTemp < dateTimePicker1.Value.Date)
{
continue;
}
#endregion
#region 获取标题
Regex regTitle = new Regex("(?<=title\\s*=\\s*\")\\s*\\S*(?<!\")");
Match matchTitle = regTitle.Match(matchs[i].Value);
if (matchTitle.Success)
{
string title = matchTitle.Value;
#region 匹配标题
bool isContain = false;
string[] keys = txt_keys.Text.Split('*');
foreach (string key in keys)
{
if (matchTitle.Value.Contains(key))
{
isContain = true;
}
}
#endregion
if (isContain)
{
//获取链接
Regex regHref = new Regex("(?<=href\\s*=\\s*\")\\s*\\S*(?<!\")");
string href = regHref.Match(matchs[i].Value).Value;
AddUI(title, href, time);
}
}
#endregion
}
}
}
}
private void AddUI(string title, string href, string time)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() => AddUI(title, href, time)));
}
else
{
ListViewItem lvitem = new ListViewItem(new string[3] { title, href, time });
listView1.Items.Add(lvitem);
}
}
实现效果
代码解析:主要是使用了HttpGet来获取链接的网页信息,然后使用正则匹配我们的需求数据。每次只访问100个讨论组,每个讨论组内获取第一页的帖子信息。如果不使用代理ip或者其他反防爬机制的话,基本上这个软件每天只使用一次就达到上限了(获取到的数据为空等);要是没有合适的帖子的话,可以第二天再查找一下。
来源:https://mp.weixin.qq.com/s/kX_48RddOBEDbhxXcEt8tg


猜你喜欢
- 前言本文主要给大家介绍了关于C#中foreach遍历的用法以及c#使用foreach需要知道的一些事,分享出来供大家参考学习,下面话不多说了
- Springboot自带定时任务实现动态配置Cron参数同学们,我今天分享一下SpringBoot动态配置Cron参数。场景是这样子的:后台
- 什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞
- 题目要求思路一:模拟迭代依次判断每个节点是否合法:左子树判断是否>low,合法就向左下走,不合法往右下;右子树判断是否<high
- 之前在项目中会用到在Java在后台把数据填入Word文档的模板来提供前台下载,为了自己能随时查看当时的实现方案及方便他人学习我写了这篇博客,
- package com.cq2022.zago.base.util;import java.io.FileReader;import jav
- IO的本质IO的作用就是从外部系统读取数据到java程序中,或者把java程序中输出的数据写回到外部系统。这里的外部系统可能是磁盘,网络流等
- idea2019导入maven项目中的某些问题idea2019导入maven项目,会出现很多莫名其妙的问题,需要注意的是如果是idea201
- 本文实例讲述了Android编程实现Listview点击展开和隐藏的方法。分享给大家供大家参考,具体如下:代码较多,所以找关键点大家贴出来,
- 1.创建项目时选择redis依赖2.修改配置文件,使用SpringBoot就避免了之前很多的xml文件2.1学过redis的同学都知道这个东
- 前言不得不说,JSP 现在已经是一门十分老旧的技术了,学习编程时,不仅要学习优秀的前言技术,还要对基础有一定的把握,所以学习 JSP 时,我
- 里氏替换原则(LSP)定义:在任何父类出现的地方都可以用它的子类类替换,且不影响功能。解释说明:其实LSP是对开闭原则的一个扩展,在OO思想
- 每一个应用都是具备一个功能,那就是版本更新,我记得我之前在面试的时候,面试官让我介绍一下应用版本更新的一些具体操作。我当时因为做过这个功能,
- 发现问题肯定有人发现连接mysql失败,然后又找不到问题所在,又出现一大最报错,如下图。解决过程 1.先查询自己的java版本,在
- 安装hbase首先下载hbase的最新稳定版本 http://www.apache.org/dyn/closer.cgi/hbas
- 一个专门实现访问sql server数据库增删改查的操作代码,分享给大家,具体内容如下using System;using System.C
- flutter组件的实现参考了react的设计理念,界面上所有的内容都是由组件构成,同时也有状态组件和无状态组件之分,这里简单介绍最基本的组
- 本文实例讲述了Java泛型定义与用法。分享给大家供大家参考,具体如下:1. 泛型的由来先看如下代码:import java.util.Lis
- 本文实例为大家分享了android实现录屏小功能的具体代码,供大家参考,具体内容如下思路android实现录屏功能有两种方案,一种是直接使用
- 本文实例讲述了Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法。分享给大家供大家参考,具体如下:情景描述:在a