爬虫(二)selenium爬二手房

news/2024/7/19 8:36:32 标签: selenium, 爬虫, 可视化, 二手房价, pyecharts

我决定爬房价。因为原本的教程,后面也是爬58同城,我也就跟着爬了。不过这次,我是基本都改了。

其实一开始很快就写好了,但是第二页开始就要输入验证码了。我查了下,说selenium可以规避这个问题,我就去安装了这个库。但是还有很多得需要提前安装。 (代码在最下面)

chromedriver

下载地址:http://chromedriver.storage.googleapis.com/index.html

我点开进去后,想着自己的谷歌是最新版本的就直接拉到最下面了。结果扑街了,2.9是2014年发布的。然后我浏览器是version67的,逐个点击了选择最新的,2.41。

解压在谷歌浏览器的目录下面。右键快捷方式 ,打开文件所在位置就好了。是一个Application的文件夹。先复制目录,等会会用到。

然后右击‘此电脑’(WIN10)–>属性–>左侧的高级系统设置–>环境变量–>看到下面有个系统变量。

点击新建,变量名随便写个’chromedrive’,然后浏览目录,选择刚才那个复制的目录(Application)。最后保存。

pyecharts">pyecharts

pyecharts是python的可视化库。我们需要按照这个库之后需要下载地图。有:

pip install echarts-countries-pypkg          # 国家地图
pip install echarts-china-provinces-pypkg    # 省的地图
pip install echarts-china-cities-pypkg       # 市的地图

我只下载了市的,因为只用来爬广州的。

了解网页结构

比如我们想要抓取广州的数据,我们可以有:http://gz.58.com/ershoufang/?PGTID=0d200001-0000-3c62-f0fc-52c0419c4d8e&ClickID=2

嗯,看起来这个url杂乱无章。但是你点击第二页就会发现它是这样的:http://gz.58.com/ershoufang/pn2/?PGTID=0d30000c-0000-3859-164a-750fda376cbf&ClickID=1

然后第三页是这样的:http://gz.58.com/ershoufang/pn3/?PGTID=0d30000c-0000-3945-5408-f27d29243517&ClickID=1

emmm,都有pn,那就试下:http://gz.58.com/ershoufang/pn1/ 。成功了!!!这是第一页的网页。祝贺。
所有第i页就是:http://gz.58.com/ershoufang/pn + str(i)

接着按照之前的方法看我们要抓取的数据的标签分别是什么。

这里写图片描述
这里写图片描述

我们要抓地区还有每平方的价格。查看之后的结果是

/html/body/div[5]/div[5]/div[1]/ul/li[1]/div[2]/p[2]/span/a[2] (地区,district)
/html/body/div[5]/div[5]/div[1]/ul/li[1]/div[3]/p[2] (价格,price)

因为位于的<div>不同,所以在抓取的时候我们还是要先抓<li>

开始爬数据
from bs4 import BeautifulSoup
from selenium import webdriver
from pyecharts import Map
import re

driver = webdriver.Chrome(executable_path='C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe')
map_district = {}

这是要用的package。
创建一个webdrive.Chrome对象,路径选择之前安装的chromedrive.exe路径。
创建一个词典,里面会存储放进去地图的值(这次用pyecharts的Map可视化)。

def get_page_num(url):
    driver.get(url)
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    pages = soup.find('div', attrs={'class': 'pager'})
    page = pages.find_all('a')[-2]
    return page

这个函数通过一个url来得到总共的页数。不过58同城都是70页,我也不知道为什么,所以你可以直接就让times=70也没关系。

还是一样像开头那样找到xpath之后根据路径来寻找。发现最后一页的路径如下:

/html/body/div[5]/div[5]/div[1]/div[2]/a[3]/span

先找到<div class='pager'>的内容,然后在里面的第二个<a>(因为属性都相同,只能find_all之后再选择导数第二个(也就是第三个,a[3])了。

需要注意的是,driver.get(url)就是发送请求了,因此page_source就是接收的网页。像上一篇文章那样用BeautifulSoup进行解析。

def get_data(url):
    driver.get(url)
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    theme = soup.find('ul', attrs={"class": "house-list-wrap"})
    Tags = theme.find_all('li')
    for li in Tags:
        try:
            li
            try:
                div_2 = li.find('div', attrs={"class": "list-info"})
                p_2 = div_2.find_all('p', attrs={'class': 'baseinfo'})[1]
                a = p_2.find_all('a', attrs={'target': '_blank'})[1]
                district = a.text.strip()

                div_3 = li.find('div', attrs={"class": "price"})
                p = div_3.find('p', attrs={'class': 'unit'}).text.strip()
                price = re.match('(.+)元/㎡', p)
                real_price = price.group(1)

                if map_district.get(district):
                    map_district[district]['num'] += 1
                else:
                    map_district[district] = {}
                    map_district[district]['num'] = 1
                    map_district[district]['price'] = 0

                map_district[district]['price'] += int(real_price)

            except:
                continue
        except:
            continue

其实也是按照xpath路径寻找到要的内容。

要解释的是,我们计算的均值是每个地区(广州有天河,白云)的价格除以检测到的该地区的数目(不是很严谨)。

比如我们检测到一个房子的district和price,首先我们先检测district(地区名)有没有在map_district里面,假如存在,则对应的数目加1,然后对应的价格也加上price。如果没有,则创建一个字典来存储这个地区,num = 1, price =0,再加上这个房子的price。

def drwa_map(name):
    name.upper()
    map = Map(name + '二手房均价', width=1200, height=600)
    names = []
    dis_price = []
    for name, dic in map_district.items():
        # this one should be changed
        if name == '广州':
            continue
        else:
            names.append(name+'区')
        dis_price.append(int(dic['price'] / dic['num']))
    # this maptype should be changed
    map.add('二手房价', attr=names, value=dis_price, maptype='广州', is_map_symbol_show=False, is_visualmap=True,
            visual_text_color='#000', visual_range=[5000, 100000])
    map.render()

这是运用了百度的pyecharts的Map,来将数据都画成地图的形式。创建一个地图,给一个名字,宽度和高度。迭代之前记录的map_district,如果name=='广州',不理会。因为地图没有广州的选项,只有各个区的选项。我也不知道为什么58不细分下去。

总价格除以总数目得出来均值,那么我们用map.add把数据加进去地图加载。maptype是选择加载的地图。is_map_symbol_show会在不同区域标记红色点,我选择了False。visual_range可以自己调整,是可以看的数据范围。

def main():
    # change the city
    city = 'gz'
    url = 'http://'+city + '.58.com/ershoufang/pn'
    # get the pages number
    times = int(get_page_num(url+'1/').text.strip())
    for i in range(1, times+1):
        url_object = url + str(i) + '/'
        get_data(url_object)
        print('正在爬取第 %s 页' % i)
    print('Total pages: {}'.format(times))
    drwa_map(city)

if __name__ == "__main__":
    main()

这是主程序。没什么好讲的了,还是要自己看的。最后会在文件目录生成一个html文件,用浏览器打开。接下来估计要学xpath和其他的库。

最终的效果:

这里写图片描述

接下来是全部的代码,要注意的是,那些文件保存的路径需要自己改:

from bs4 import BeautifulSoup
from selenium import webdriver
from pyecharts import Map
import re

driver = webdriver.Chrome(executable_path='C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe')
map_district = {}


def get_page_num(url):
    driver.get(url)
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    pages = soup.find('div', attrs={'class': 'pager'})
    page = pages.find_all('a')[-2]
    return page


def get_data(url):
    driver.get(url)
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    theme = soup.find('ul', attrs={"class": "house-list-wrap"})
    Tags = theme.find_all('li')
    for li in Tags:
        try:
            li
            try:
                div_2 = li.find('div', attrs={"class": "list-info"})
                p_2 = div_2.find_all('p', attrs={'class': 'baseinfo'})[1]
                a = p_2.find_all('a', attrs={'target': '_blank'})[1]
                district = a.text.strip()

                div_3 = li.find('div', attrs={"class": "price"})
                p = div_3.find('p', attrs={'class': 'unit'}).text.strip()
                price = re.match('(.+)元/㎡', p)
                real_price = price.group(1)

                if map_district.get(district):
                    map_district[district]['num'] += 1
                else:
                    map_district[district] = {}
                    map_district[district]['num'] = 1
                    map_district[district]['price'] = 0

                map_district[district]['price'] += int(real_price)

            except:
                continue
        except:
            continue


def drwa_map(name):
    name.upper()
    map = Map(name + '二手房均价', width=1200, height=600)
    names = []
    dis_price = []
    for name, dic in map_district.items():
        # this one should be changed
        if name == '广州':
            continue
        else:
            names.append(name+'区')
        dis_price.append(int(dic['price'] / dic['num']))
    # this maptype should be changed
    map.add('二手房价', attr=names, value=dis_price, maptype='广州', is_map_symbol_show=False, is_visualmap=True,
            visual_text_color='#000', visual_range=[5000, 100000])
    map.render()


def main():
    # change the city
    city = 'gz'
    url = 'http://'+city + '.58.com/ershoufang/pn'
    # get the pages number
    times = int(get_page_num(url+'1/').text.strip())
    for i in range(1, times+1):
        url_object = url + str(i) + '/'
        get_data(url_object)
        print('正在爬取第 %s 页' % i)
    print('Total pages: {}'.format(times))
    drwa_map(city)


if __name__ == "__main__":
    main()

http://www.niftyadmin.cn/n/1364279.html

相关文章

[转载]DateTime TryParse

今天被Architect问住了&#xff0c;说你光用一个TryParse就判断人家是不是时间日期型的&#xff0c;是不完整的。所以我花点时间看了下TryParse的用法。 MSDN&#xff1a;http://msdn.microsoft.com/en-us/library/system.datetime.tryparse.aspx TryParse有两种格式&#xff0…

Spring之BeanFactory

Spring 官方文档 Spring Framework Documentation【Version 5.1.4.RELEASE】BeanFactory API DocBeanFactory 接口提供了一种能够管理任何类型对象的高级配置机制BeanFactory用于访问Spring bean容器的根接口。这是bean容器的基本客户端视图; 进一步的接口&#xff0c;如Lista…

激活函数(Sigmoid, tanh, Relu)

重新看了一下&#xff0c;发现写的时候思绪乱了也没有检查。现在重新更改&#xff0c;特别是输出不以零为中心(non-zero center)的问题。 按照进程这章总结的是激活函数&#xff08;Activation Function&#xff09;。其实这节CS231n讲的就很细了&#xff0c;但是&#xff01;&…

js正則表達式语法

1. 正則表達式规则 1.1 普通字符 字母、数字、汉字、下划线、以及后边章节中没有特殊定义的标点符号&#xff0c;都是"普通字符"。表达式中的普通字符&#xff0c;在匹配一个字符串的时候&#xff0c;匹配与之同样的一个字符。 举例1&#xff1a;表达式 "c&q…

Spring之ApplicationContext

Spring 官方文档 Spring Framework Documentation【Version 5.1.4.RELEASE】ApplicationContext API Doc 介绍 ApplicationContext用于为应用程序提供配置的中央接口。这在应用程序运行时是只读的&#xff0c;但如果实现支持&#xff0c;则可以重新加载 ApplicationContext提…

As的完全倒装句

在韩刚的三级笔译的书里面看到这么一句&#xff1a; Its overall national strength has considerably increased, as has its international standing and influence. 虽然知道这句话的意思&#xff0c;但没有接触过as的完全倒装句&#xff0c;也是查了一下资料才能确定自己…

GET传值

发送页&#xff1a; 1 <form id"form1" runat"server"> 2 <div> 3 <asp:TextBox ID"TextBox1" runat"server">008</asp:TextBox> 4 <asp:Button ID"Button1" runat"server" OnClick&q…

JDK5可变参数(Varargs)

源码 package sto.pdd.util;public class VariableParameter {/* 求若干个整型数中的最大值 * 可变参数items*/public static int getMax(int... items){ int max Integer.MIN_VALUE; for(int item : items){ max item > max? item : max; } return…