2023年11月28日发(作者:)

从零开始学习python爬⾍(⼀):获取58同城⼆⼿房信息

⼤数据时代下,编写爬⾍程序已经成为信息收集的必备技能;python在数据挖掘⽅⾯具有极⼤优势且简单易学,是新⼿⼊坑爬⾍程序编

写的极佳语⾔。

由于在校期间本⼈主要应⽤javamatlab进⾏数据挖掘,因此借助刚⼊职的学习期,简单开发了⼀个最基本的python爬⾍获取58同城⼆

⼿房信息,⼀来是⾃⼰借此练⼿python和爬⾍开发,⼆来是爬取的数据可以实际⽤于⾃⼰之后的学习,也算是做个⼩⼩的预研吧。在两个⼯

作⽇的开发后,终于实现了⽤⾃⼰开发的爬⾍在58同城上爬取了1500条本地⼆⼿房数据。这篇随笔将介绍这个简单pyhon爬⾍的实现过程、

及需要学习的知识和搭建的环境,希望能给同样刚接触python和爬⾍开发的学习者们⼀点点参考,存在不⾜请⼤家批评指正。

开发前准备:

必备知识:python基本语法、web前端基本知识、数据库基本知识

要编写爬⾍程序,必要的python语法知识还是不可少的。python简单易学,对于⽤过其他开发语⾔的开发者来说能很快上⼿(当然,只

是简单上⼿)。然后由于我们要从⽹页上爬取,所以对web前端相关知识还是需要⼤致了解下;最后我们要将爬取的数据存⼊数据库(本次

开发采⽤oracle),所以数据库基本知识也是不可少的。

搭建环境:pythonoraclepycharm

pythonoracle的按照这⾥不再过多叙述,⽹上的资料已经有⾮常之多。本次开发python选择的是2.7版本,开发⼯具我尝试了pycharm

jupyter notebook,可以说是各有千秋,实际开发中使⽤pycharm的⼈应该还是更多的,同样安装配置过程在⽹上也可以轻松找到。需要注

意的是oracle字符集的清晰设定可以避免之后的开发出现乱码的坑。

整体思路:

我们的⽬标是从⽹页中爬取需要的资料,所以整个开发思路可以按此递进展开:1.获得某⼀页⾯的⽂本数据,截取需要的字段;2.将截

取的字段进⾏整理,存放在数据库中;3.实现爬取多页或者某⼀列表的多条信息;4.组合完成⼀个可以按页依次爬取58同城⼆⼿房信息,并

存⼊oracle的简单爬⾍。思路明确后,接下来便介对其进⾏实现:

第⼀步,先安装好必要的模块

cmd下进⼊pythonscripts⽂件夹,按照BeautifulSoup4requests这两个模块。这两个模块都是python开发爬⾍的利

器,BeautifulSoup4⽤于从⽹页抓取数据,⽽requests则⽤于进⾏HTTP连接。具体操作如下。

cd C:Python27Scripts

pip install BeautifulSoup4

pip install requests

除此之外,还有pandassqlalchemy re需要安装(同样也是pip install),pandas⽤来整理数据、sqlalchemy ⽤来访问数据库、re

来实现正则表达式。安装好后,我们new⼀个python⽂件,在⽂件开头导⼊。

import requests

import pandas

import re

from bs4 import BeautifulSoup

from sqlalchemy import create_engine

设定os编码可以避免之后数据库的乱码

import os

n['NLS_LANG'] = 'SIMPLIFIED CHINESE_8'

第⼆步,获得某⼀页⾯的⽂本数据

在爬取前,我们应该先明确爬取的原理。在google chrome打开开发者⼯具,访问58同城⼆⼿房页码,查看⽹页源代码,我们可以发现

我们需要的字段都包含在各个标签⾥。

·············

我们的可以先将这些html代码获取下来,再进⾏筛选取得我们需要的字段。这⾥我定义了⼀个函数,传⼊⽹页的url,采⽤requests进⾏连

接,然后在⽤BeautifulSoup获取到⽂本的信息。

new_message_total = get_message_total('url')

df = ame(new_message_total)

def get_message_total(message_uri):

res = (message_uri)

ng = 'utf-8'

soup = BeautifulSoup(, '')

通过观察,我们发现每⼀套房屋信息中我们需要的⼤部分字段都包含在⼀个list-infoclass⾥,于是我们便可以利⽤获取所

list-info的信息,具体的⽤法可以去看看BeautifulSoup的相关⽂档,我们输出了('.list-info')的数组长度,即每⼀页出现房屋信息

的次数;同时定义⼀个message_total为之后的存储预留空间。

def get_message_total(message_uri):

res = (message_uri)

ng = 'utf-8'

soup = BeautifulSoup(, '')

num = len(('.list-info'))

message_total = []

print(num)

第三步,将截取的字段进⾏整理

这⾥建⽴了⼀个循环去获取每个('.list-info')数组的元素,即每个房屋信息,如('.list-info')[0]即第⼀条房屋信息。接

下来再⽤获取每个需要的字段,然后对字段进⾏了简单处理(字段去掉空格换⾏,⾯积价格去单位),这⾥我⼏乎没有⽤到正则

表达式。取得的字段我们存放在⼀个result⾥,在每次循环结束⼀并存放到message_total中,在获取全部的数据后我们将其作为函数的返

回。

for i in range(0, num):

message = ('.list-info')[i]

pricetext = ('.price')[i]

title = ('a')[0].e('t', '').replace('n', '').replace(' ', '')

housetype = ('span')[0].e('t', '').replace('n', '').replace(' ', '')

areatext = ('span')[1].e('t', '').replace('n', '').replace(' ', '')

area=l(r"d+.?d*",areatext)[0]

orientation = ('span')[2].e('t', '').replace('n', '').replace(' ', '')

floor = ('span')[3].e('t', '').replace('n', '').replace(' ', '')

estate = ('a')[1].e('t', '').replace('n', '').replace(' ', '')

region = ('a')[2].e('t', '').replace('n', '').replace(' ', '')

position = ('a')[3].e('t', '').replace('n', '').replace(' ', '')

totalpricetext = ('p')[0].e('t', '').replace('n', '').replace(' ', '')

totalprice=l(r"d+.?d*",totalpricetext)[0]

unitpricetext= ('p')[1].e('t', '').replace('n', '').replace(' ', '')

unitprice=l(r"d+.?d*",unitpricetext)[0]

result = {}

result['title'] = title

result['housetype'] = housetype

result['area'] = area

result['orientation'] = orientation

result['floor'] = floor

result['estate'] = estate

result['region'] = region

result['position'] = position

result['totalprice'] = totalprice

result['unitprice'] = unitprice

message_(result)

return message_total

第四步,将数据存⼊数据库中 我们获取返回的数组后,采⽤pandas将其进⾏整理,然后建⽴与数据库的连接,采⽤to_sql⽅法将整

dateframe存⼊数据库。to_sql⽅法有多参数可以设定,很重要的⼀个是if_exists,如果设为'append'则表⽰表如果存在则进⾏插⼊操作。

new_message_total = get_message_total(url')

df = ame(new_message_total)

print(df)

db_engine=create_engine('oracle://name:password@url/databasename')

_sql(name=tablename, con=db_engine, if_exists='append', index=False)

第五步,实现实现爬取多页或者某⼀列表的多条信息

多页爬取的实现⽐较简单,将url中关于页码的参数进⾏赋值(在这⾥为/ershoufang/pn,pn1表⽰第⼀页第⼆页)存放到⼀个数

组中,接着循环调⽤get_message_total函数去查询整个url数组即可。⽽要爬取某⼀页中的每个房产信息的详细情况则(多层次的爬取)⽐

较⿇烦。⼀个⽅法是采⽤获取相应标签中记录房产信息详情的地址,然后将其url记录,⽤和多页爬取相同的⽅法进⾏爬取。

第⼆种⽅法是在开发者⼯具-Network-js中进⾏寻找可能存放urljs⽂件,我们发现这边的query_list中涵盖了多个房产信息详情的地址,我们

将其获取去掉开头的函数名和括号及结尾的括号,获得⼀个json字符串,再将json字符串中每个url获取出来进⾏记录,然后在⽤

get_message_total函数去查询整个url数组。

res = (

'/aurora/')

ng = 'utf-8'

num = 0

jd = (('jQuery112427407_33(').rstrip(')''))

for ent in jd['data']['houseList']:

print num

num = num + 1

res = (jd['data']['houseList'][0]['url'])

ng = 'utf-8'

soup = BeautifulSoup(, '')

print()

第六步,整合

这⾥我只将爬取单个页⾯的源码全部粘贴出来,不将所有代码贴出的原因是同城的反爬⾍机制使得⽬前的多列表查询或者多页查询形同

虚设,且思路明确后多页查询和多列表查询的第⼀种⽅法只是在重复之前的⼯作,⽽多列表查询第⼆种⽅法的关键代码也已经贴出。有需要

的同学改下数据库参数便可⽤于爬取58⼆⼿房信息,将中间的获取规则部分进⾏修改后(可以⽤正则表达式,会简单很多)也可以⽤于其他

场景。

# encoding: utf-8

import requests

import pandas

import re

from bs4 import BeautifulSoup

from sqlalchemy import create_engine

import os

n['NLS_LANG'] = 'SIMPLIFIED CHINESE_8'

def get_message_total(message_uri):

res = (message_uri)

ng = 'utf-8'

soup = BeautifulSoup(, '')

num = len(('.list-info'))

message_total = []

print(num)

for i in range(0, num):

message = ('.list-info')[i]

pricetext = ('.price')[i]

title = ('a')[0].e('t', '').replace('n', '').replace(' ', '')

housetype = ('span')[0].e('t', '').replace('n', '').replace(' ', '')

areatext = ('span')[1].e('t', '').replace('n', '').replace(' ', '')

area=l(r"d+.?d*",areatext)[0]

orientation = ('span')[2].e('t', '').replace('n', '').replace(' ', '')

floor = ('span')[3].e('t', '').replace('n', '').replace(' ', '')

estate = ('a')[1].e('t', '').replace('n', '').replace(' ', '')

region = ('a')[2].e('t', '').replace('n', '').replace(' ', '')

position = ('a')[3].e('t', '').replace('n', '').replace(' ', '')

totalpricetext = ('p')[0].e('t', '').replace('n', '').replace(' ', '')

totalprice=l(r"d+.?d*",totalpricetext)[0]

unitpricetext= ('p')[1].e('t', '').replace('n', '').replace(' ', '')

unitprice=l(r"d+.?d*",unitpricetext)[0]

result = {}

result['title'] = title

result['housetype'] = housetype

result['area'] = area

result['orientation'] = orientation

result['floor'] = floor

result['estate'] = estate

result['region'] = region

result['position'] = position

result['totalprice'] = totalprice

result['unitprice'] = unitprice

message_(result)

return message_total

new_message_total = get_message_total(url')

df = ame(new_message_total)

print(df)

db_engine=create_engine('oracle://name:password@url/databasename')

_sql(name=tablename, con=db_engine, if_exists='append', index=False)

下⼀步:⽬前的爬⾍程序还只是⼀个简单的⼩demo,⾸要解决的还是各⼤⽹站的反爬⾍限制问题。⽐如58同城爬取数据中,每爬取⼏

⼗条数据便会要求输验证码,这使得我们的多页爬取失去了意义(每爬取⼀两页就要⼿动输⼊验证码),⽽安居客则会直接检测爬⾍程序

(之后可能会采⽤伪装成浏览器的⽅法进⾏解决)。因为我们不可能像⼀些平台那样采⽤ip池的⽅法避免ip限制,所以还需加深对反反爬⾍

的研究。(如果接下来还有需要及空闲时间,下⼀篇随笔可能就是相关内容,但是⾃⼰感觉⼤概率要鸽...)此外,多线程爬取、采集数据的

增量处理等都是需要深⼊研究的内容。

感悟:从熟悉python语法到开发完成花了两个⼯作⽇,很⼤⼀部分时间都⽤在解决各种坑上(搭建环境、更改数据库字符集等),这段

时间的学习我确实感受到python的简单好⽤,但是我⽬前对python还停留在初步认识的阶段,很多要⽤到的知识点都是遇到了再去查;因为

时间紧凑,⽬前只是按照之前的编程思路运⽤python这个⼯具将⾃⼰的⽬标实现,对python的特性还有待探索,如果能善⽤正则表达式和⼀

些⾼效的模块,想必开发能简单不少。虽然本⼈很早就接触博客园,但是这是我在博客园的第⼀篇⽂章,主要也是为了记录⾃⼰刚刚开始的

⼯作⽣涯中对各种技术的探索,同时也希望能和⼤家⼀起交流学习,为同样的萌新提供点参考,当然我更欢迎⼤家提出意见及批评指正。最

后,本⼈爬取的数据仅⽤于个⼈的学习,绝不⽤于商业⽤途。