郑重说明:本爬虫仅限个人学习研究,非商业、非盈利,请合规使用爬虫,否则违规爬取由此产生的一切后果,请自行承担,与本人无关
Hello,大家好啊!我是亮娃儿
今天给大家分享一下如何 用Python自动化爬取多页链家二手房数据✅
包含标题、户型、面积、单价、朝向、楼层、装修、小区名称、商圈、总价、建成年份 等几部分信息,希望对大家有所帮助,如有疑问或者需要改进的地方可以联系我。
本次爬取目标站点:成都成华区链家二手房
目标网址:https://cd.lianjia.com/ershoufang/chenghua/
该页面为 静态网页,数据结构规整、元素清晰,非常适合新手练习爬虫项目。

按 F12调出浏览器的开发者工具分析数据位置,这里我们用到的是 Google浏览器,所有二手房房源数据,全部规整存放在页面 li 标签 中,可通过CSS选择器精准定位!

这里可以看到二手房房源的标题信息,在a标签的text里:
其余房源标题、详情链接、价格、户型等核心数据,同理
本次使用 DrissionPage 自动化模块,无需复杂配置,语法简洁、适配性强,完美适配静态网页爬取。
# 导入自动化模块from DrissionPage import ChromiumPage# 实例化浏览器对象dp = ChromiumPage()# 访问网站dp.get('https://cd.lianjia.com/ershoufang/chenghua/')💡 小提示:不熟悉 DrissionPage 模块的小伙伴,可以参考我的往期实战文章:(《用Python抓取《疯狂动物城2》豆瓣影评》)
这里使用DrissionPage里CSS选择器进行元素定位,匹配多个标签
info里面的所有数据都在一起所以 "|" 做了一个分割,分割后会变成一个列表
最后再用一个字典,把所有数据写成键-值对的形式,并写入到writer中,为下一步保存数据做好铺垫 ✅
lis = dp.eles('css:.sellListContent li') # print(lis) # for循环遍历,提取列表中的元素for li in lis: # 在循环中提取具体数据内容 title = li.ele('css:.info .title a').text href = li.ele('css:.info .title a').attr('href') totalPrice = li.ele('css:.info .priceInfo .totalPrice span').text unitPrice = li.ele('css:.info .priceInfo .unitPrice').text community = li.ele('css:.info .flood .positionInfo a').text area = li.ele('css:.info .flood .positionInfo a:last-of-type').text info = li.ele('css:.houseInfo ').text.split('|') print(info) dit = {'标题': title,'户型': info[0],'面积': info[1],'单价': unitPrice,'朝向': info[2],'楼层': info[4],'装修': info[3],'小区名称': community,'商圈': area,'总价': totalPrice,'年份': info[5] iflen(info)== 7else'未知','建筑结构': info[-1],'详情页': href, } # 写入数据 csv_writer.writerow(dit) print(dit)将提取的结构化数据,批量写入CSV文件,可直接用Excel打开查看、筛选、分析数据。
# 导入csv模块import csv# 创建并初始化csv文件f = open('data.csv', mode='w', encoding='gbk', newline='')# 定义表头字段csv_writer = csv.DictWriter( f, fieldnames=['标题','户型','面积','单价','朝向','楼层','装修','小区名称','商圈','总价','年份','建筑结构','详情页'])# 写入表头csv_writer.writeheader()# 初始化空字典存储数据dit = {}csv_writer.writerow(dit)链家翻页会触发人机点选验证码,也是本次爬虫的 核心难点!
常见验证码类型分为3类:



本次借助 云码打码平台 实现全自动识别破解,接口稳定、准确率高:👇
平台地址:https://www.jfbym.com/price.html

调用平台 API接口,填入相关参数:

个人中心可查看专属 Token密钥,替换即可使用:

✅ 验证码坐标识别核心代码:
# 导入时间模块import time# 导入编码模块import base64# 识别接口 url = 'http://api.jfbym.com/api/YmServer/customApi' # 获取图片内容 img_content = open('bg.jpg', 'rb').read() # 转成base64 img_base64 = base64.b64encode(img_content) # 请求参数(语序) data = {'image': img_base64,'token': '填入自己的token值','type': '30116', } # 请求获取坐标 result = requests.post(url=url, data=data).json() # 请求参数(符号点选) data_1 = {'image': img_base64,'token': '填入自己的token值','type': '30009', } # 请求获取坐标 result_1 = requests.post(url=url, data=data_1).json()if result['msg'] == '识别成功'and len(result['data']['data'].split('|')) == 3: print(result)return result['data']['data'] elif result_1['msg'] == '识别成功'and len(result_1['data']['data'].split('|')) == 3: print(result_1)return result_1['data']['data']else: # 请求参数(符号点选) data_2 = {'image': img_base64,'extra': 'je4_click','token': '填入自己的token值','type': '30112', } # 请求获取坐标 result_2 = requests.post(url=url, data=data_2).json()return result_2['data']['data']通过动作链自动点击验证码坐标,完成验证,添加重试机制,避免单次识别失败导致程序中断。
# 导入动作链from DrissionPage.common import Actions# 导入时间模块import time"""做验证码识别""" # 点击验证 -->需要一定时间验证识别 dp.ele('text=点击按钮开始验证').click() # 延时验证 time.sleep(4) # 定位验证码图片标签 img = dp.ele('css:.geetest_box') # 截图保存验证码图片 img.get_screenshot(path='bg.jpg') # 获取坐标 x_y = get_code().split('|')for i in x_y: x = int(i.split(',')[0]) y = int(i.split(',')[1]) ac.move_to(img, offset_x=x, offset_y=y).click() # 点击确定 dp.ele('text=确定').click() # 下滑页面到底部 dp.scroll.to_bottom()if dp.url != 'captcha': get_content()return'成功'else:return'重试'设置翻页循环,自动判断验证码、失败重试、循环采集多页数据,实现全自动批量爬取。
# 构建循环翻页for page in range(1, 6):print(f'正在采集第{page}页的数据内容') # 获取网页链接 link = dp.url # 判断是否出现验证码if'captcha' in link: .....# 导入自动化模块from DrissionPage import ChromiumPage# 导入csv模块import csv# 导入时间模块import time# 导入编码模块import base64# 导入数据请求模块import requests# 导入动作链from DrissionPage.common import Actions# 获取页面数据def get_content(): # 提取所有房源信息所在的li标签 ---> 返回的是列表 lis = dp.eles('css:.sellListContent li') # print(lis) # for循环遍历,提取列表中的元素for li in lis: # 在循环中提取具体数据内容 title = li.ele('css:.info .title a').text href = li.ele('css:.info .title a').attr('href') totalPrice = li.ele('css:.info .priceInfo .totalPrice span').text unitPrice = li.ele('css:.info .priceInfo .unitPrice').text community = li.ele('css:.info .flood .positionInfo a').text area = li.ele('css:.info .flood .positionInfo a:last-of-type').text info = li.ele('css:.houseInfo ').text.split('|') print(info) dit = {'标题': title,'户型': info[0],'面积': info[1],'单价': unitPrice,'朝向': info[2],'楼层': info[4],'装修': info[3],'小区名称': community,'商圈': area,'总价': totalPrice,'年份': info[5] iflen(info)== 7else'未知','建筑结构': info[-1],'详情页': href, } # 写入数据 csv_writer.writerow(dit) print(dit)# 获取坐标def get_code(): # 识别接口 url = 'http://api.jfbym.com/api/YmServer/customApi' # 获取图片内容 img_content = open('bg.jpg', 'rb').read() # 转成base64 img_base64 = base64.b64encode(img_content) # 请求参数(语序) data = {'image': img_base64,'token': '填入自己的token值','type': '30116', } # 请求获取坐标 result = requests.post(url=url, data=data).json() # 请求参数(符号点选) data_1 = {'image': img_base64,'token': '填入自己的token值','type': '30009', } # 请求获取坐标 result_1 = requests.post(url=url, data=data_1).json()if result['msg'] == '识别成功'and len(result['data']['data'].split('|')) == 3: print(result)return result['data']['data'] elif result_1['msg'] == '识别成功'and len(result_1['data']['data'].split('|')) == 3: print(result_1)return result_1['data']['data']else: # 请求参数(符号点选) data_2 = {'image': img_base64,'extra': 'je4_click','token': '填入自己的token值','type': '30112', } # 请求获取坐标 result_2 = requests.post(url=url, data=data_2).json()return result_2['data']['data']# 验证获取数据def get_check(): """做验证码识别""" # 点击验证 -->需要一定时间验证识别 dp.ele('text=点击按钮开始验证').click() # 延时验证 time.sleep(4) # 定位验证码图片标签 img = dp.ele('css:.geetest_box') # 截图保存验证码图片 img.get_screenshot(path='bg.jpg') # 获取坐标 x_y = get_code().split('|')for i in x_y: x = int(i.split(',')[0]) y = int(i.split(',')[1]) ac.move_to(img, offset_x=x, offset_y=y).click() # 点击确定 dp.ele('text=确定').click() # 下滑页面到底部 dp.scroll.to_bottom()if dp.url != 'captcha': get_content()return'成功'else:return'重试'# 创建文件对象f = open('data.csv', mode='w', encoding='gbk', newline='')# 字典写入的方法csv_writer = csv.DictWriter(f, fieldnames=['标题','户型','面积','单价','朝向','楼层','装修','小区名称','商圈','总价','年份','建筑结构','详情页'])# 写入表头csv_writer.writeheader()# 实例化浏览器对象dp = ChromiumPage()ac = Actions(dp)# 访问网站dp.get('https://cd.lianjia.com/ershoufang/chenghua/')# 构建循环翻页for page in range(1, 21):print(f'正在采集第{page}页的数据内容') # 获取网页链接 link = dp.url # 判断是否出现验证码if'captcha' in link: # 验证码重试逻辑 max_retries = 3for retry in range(max_retries): check_result = get_check()if check_result == '成功':break elif retry < max_retries - 1: print(f"验证码失败,第{retry + 2}次重试...") time.sleep(2)else: print("验证码连续失败,跳过该页")breakelse: # 下滑页面到底部 dp.scroll.to_bottom() # 获取网页链接 get_content() # 点击下一页按钮(需要检查元素是否存在)try: next_page = dp.ele('css:.page-box a:last-of-type', timeout=3)if next_page: next_page.click() time.sleep(2) except Exception as e: print(f"翻页失败: {e}")break📊 最后生成的数据展示:
程序运行完成后,爬了20页的数据,自动生成 data.csv 文件,所有二手房数据规整入库,可直接用于数据分析、数据可视化!后续我会这个数据进行一个可视化的分析,敬请期待!

实测效果:
关注公众号,后台留言我,即可获取相关代码,我的微信:shangliang87

✅ - 《夜神模拟器自动化爬取抖音用户信息数据实战:uiautomator2+Fiddler+Mitmproxy完美组合》
QQ:892186377,随时互动、提建议。另有交流群(内含资料),欢迎加入!

未完待续,欢迎关注!🥰动动您发财的小手点个赞吧 👍 !欢迎转发!🔁