引言

网络爬虫就像一个自动的小机器人,它可以根据我们给它的规则,自动地从互联网上抓取网页、图片、视频等内容。它的基本流程如下:

首先,它会像浏览器一样,发送一个请求给指定的网站服务器,请求服务器给它返回网页的数据。

目标与工具准备

  • 爬取目标:获取58同城成都商铺出租转让第4页的基础信息(标题、区域、面积、租金)和详情页信息(转让费、联系人、二维码链接)。
  • 依赖工具:
    • Python库:requests(发送HTTP请求)、BeautifulSoup(解析HTML)、pandas(数据存储)。
    • 安装命令:
      pip install requests beautifulsoup4 pandas openpyxl
      

分析网页

请求头构造

模拟浏览器访问需设置请求头,关键字段包括:

  • User-Agent:标识浏览器类型(如Chrome)。
  • Cookie:从浏览器开发者工具的Network标签中复制(需登录状态)。
import requests
from bs4 import BeautifulSoup

URL = 'https://cd.58.com/shangpucz/0/pn4/?skuInserted=0&FGTID=109397958221330411064287535&maxInserted=27&gposLastIndex=88&zzvipInserted=0'
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67',
    'cookie': 'f=n; id58=CkwANGkoWfBebjKeCu4AAg==; wmda_uuid=46fcd9de7a42c1345c1c26763b2d9dc1; wmda_new_uuid=1; wmda_session_id_29317198571852=1764252144048-fe9da707-b2fb-48ad-befb-9c41593d4e8b; wmda_visited_projects=%3B29317198571852; wmda_session_id=1764252144130-5b68f4ab-d1e1-4565-8b26-ccee502f4d78; 58tj_uuid=380c661c-2aa7-43cc-8a86-83ddb6ecf9e1; new_uv=1; xxzlclientid=08c8d85b-4f4a-47fa-ac5c-1764252144462; als=0; xxzlxxid=pfmxiw0B2t+gimcGgzt4n/oWWVSAG6aKjfXw/wBnNbIJTuFSIPg7ioW+g38hUABHbEIC; utm_source=zhaopin_B; www58com="UserID=116429460129353&UserName=ambu4hugh"; 58cooper="userid=116429460129353&username=ambu4hugh"; 58uname=ambu4hugh; passportAccount="atype=0&bstate=0"; new_session=0; wmda_report_times=40; spm=u-2gn98rpfn988mb62t1.2gn99heh13vhwqfjjkg; 58home=nc; f=n; commontopbar_new_city_info=669%7C%E5%8D%97%E6%98%8C%7Cnc; city=nc; commontopbar_ipcity=nc%7C%E5%8D%97%E6%98%8C%7C0; init_refer=https%253A%252F%252Fwww.so.com%252Flink%253Fm%253DeOmptoAMqVCMapI8fmIxrYPH%25252BH89dOrTmgBYBzQkYG9Wh4AwymZD1uxCKbvBFj5VjHPw0J93oBsT6cyLck%25252FKOUUMOWgkf8lXl0nCb1WKx6p%25252BaAwGgkXhJ%25252Fw%25253D%25253D; PPU=UID=116429460129353&UN=ambu4hugh&TT=0607959b2a1610a211b47a872eaa4729&PBODY=Ett-_jvIXly5Oi0Aif1CQiBpLeaKUCUXm8RfBBrq_bpq1kWUQ-8u7vA_xVzYPA0pXFzNPseUK9tSHsHn9nMG706LGcFknesfg5PVbvl1XHRGyi7i4GV1DCSMys-SVMz8YzwqW0_2Oj-fk6-bRYL5sOEFCOavBeKJMxEbdi-eZew&VER=1&CUID=Rai0Bv0vso1BsNZPMtzPww; fzq_h=30a4c67081e204b3bab466b5c1e857bc_1764252215942_a052a03fe3144d2fa748436a589c13c2_1867122615; JSESSIONID=918AD181FC8A99E665D3577E42C4D604; xxzlbbid=pfmbM3wxMDI5OXwxLjExLjB8MTc2NDI1MjIyMDI4NDY0MDk4MHx0OEorT3VOSzRncWN6ekViS3JJdXVUQU9NT1pacGlxVFZzaDlVN05kMVhrPXx8'
}

发送请求与解析HTML

发送GET请求并解析返回的HTML内容:

res = requests.get(URL, headers=headers)
html = BeautifulSoup(res.content, 'html.parser')
print(html)  # 验证是否成功获取

提取基础信息

        打开 58 同城商铺页面 → F12 打开开发者工具 → 点击 “元素选择器”(左上角箭头),
        点击任意商铺标题 → 右侧会高亮对应的 HTML 标签:可看到标题在span标签中,class="title_des"

通过开发者工具定位元素标签和类名,提取数据:

  • 商铺标题:<span class="title_des">
  • 区域/地址:<span class="withi">
  • 面积:<p class="num">
  • 租金:<span class="num">

title_des = html.find_all('span', class_="title_des")
withi = html.find_all('span', class_="withi")
squa = html.find_all('p', class_='num')
price = html.find_all('span', class_='num')

titles = [ti.text for ti in title_des]
area_address = [wi.text for wi in withi]
areas = [item for index, item in enumerate(area_address) if index%2==0]
addresses = [item for index, item in enumerate(area_address) if index%2!=0]
square = [sq.text.replace('\n','') for sq in squa]

import re
rent = []
for pr in price:
    pri = pr.text.replace('\n','')
    match = re.search(r'(\d+(\.\d+)?)', pri)
    if match:
        pri = pri.replace(match.group(), f'{match.group()}万/月')
    rent.append(pri)

提取详情页信息

  1. 获取详情页链接:
    商铺条目通常包裹在li标签中,详情页入口是li内的a标签(href属性为链接)

    links = []
    li_list = html.find_all('li')
    for li in li_list:
        a_tag = li.find('a')
        if a_tag:
            links.append(a_tag['href'])
    links = [link for link in links if len(link)>=200]  # 过滤无效链接
    

  2. 爬取详情页数据:
    遍历链接,请求详情页并提取转让费、联系人、二维码链接:

    fee, names, tels = [], [], []
    for lin in links[:3]:  # 测试前3个链接
        resp = requests.get(lin, headers=headers)
        detail_html = BeautifulSoup(resp.content, 'html.parser')
        
        transfer_fee = detail_html.find('span', class_="house_basic_title_money_num_second")
        contact_name = detail_html.find_all('span', class_="name-text")
        qr_code = detail_html.find('img', class_="qr")
        
        names.extend([na.text for na in contact_name][1::2])  # 去重取奇数位
        tels.append(qr_code.get('src') if qr_code else None)
        fee_text = transfer_fee.text.replace('\n','').replace(' ','') if transfer_fee else '-'
        fee.append(fee_text.replace('转让费:','') if '转让费' in fee_text else '-')
    

数据清洗与存储

  • 转让费格式化:处理“面议”或缺失值。
  • 存储为Excel(需pandas):
    import pandas as pd
    data = {
        '标题': titles,
        '区域': areas,
        '面积': square,
        '租金': rent,
        '转让费': fee[:len(titles)],  # 对齐数据长度
        '联系人': names[:len(titles)],
        '联系方式': tels[:len(titles)]
    }
    df = pd.DataFrame(data)
    df.to_excel('58同城商铺数据.xlsx', index=False)
    

完整代码

# -*- coding: utf-8 -*-  # 设置Python文件编码为UTF-8,支持中文显示
import requests  # 用于发送HTTP请求,获取网页内容
from bs4 import BeautifulSoup  # 用于解析HTML网页结构,提取目标数据
import pandas as pd  # (预留:可用于后续将数据整理为表格)
import re  # 用于正则表达式匹配,处理租金等文本格式
import openpyxl  # (预留:可用于后续将数据写入Excel)

# 目标爬取链接:58同城成都商铺出租转让第4页的页面地址
URL = 'https://cd.58.com/shangpucz/0/pn4/?skuInserted=0&FGTID=109397958221330411064287535&maxInserted=27&gposLastIndex=88&zzvipInserted=0'

# 构造请求头:模拟浏览器访问,避免被网站反爬
# User-Agent:标识浏览器类型;Cookie:模拟用户登录状态(从浏览器开发者工具中复制)
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67',
    'cookie': 'f=n; id58=CkwANGkoWfBebjKeCu4AAg==; wmda_uuid=46fcd9de7a42c1345c1c26763b2d9dc1; wmda_new_uuid=1; wmda_session_id_29317198571852=1764252144048-fe9da707-b2fb-48ad-befb-9c41593d4e8b; wmda_visited_projects=%3B29317198571852; wmda_session_id=1764252144130-5b68f4ab-d1e1-4565-8b26-ccee502f4d78; 58tj_uuid=380c661c-2aa7-43cc-8a86-83ddb6ecf9e1; new_uv=1; xxzlclientid=08c8d85b-4f4a-47fa-ac5c-1764252144462; als=0; xxzlxxid=pfmxiw0B2t+gimcGgzt4n/oWWVSAG6aKjfXw/wBnNbIJTuFSIPg7ioW+g38hUABHbEIC; utm_source=zhaopin_B; www58com="UserID=116429460129353&UserName=ambu4hugh"; 58cooper="userid=116429460129353&username=ambu4hugh"; 58uname=ambu4hugh; passportAccount="atype=0&bstate=0"; new_session=0; wmda_report_times=40; spm=u-2gn98rpfn988mb62t1.2gn99heh13vhwqfjjkg; 58home=nc; f=n; commontopbar_new_city_info=669%7C%E5%8D%97%E6%98%8C%7Cnc; city=nc; commontopbar_ipcity=nc%7C%E5%8D%97%E6%98%8C%7C0; init_refer=https%253A%252F%252Fwww.so.com%252Flink%253Fm%253DeOmptoAMqVCMapI8fmIxrYPH%25252BH89dOrTmgBYBzQkYG9Wh4AwymZD1uxCKbvBFj5VjHPw0J93oBsT6cyLck%25252FKOUUMOWgkf8lXl0nCb1WKx6p%25252BaAwGgkXhJ%25252Fw%25253D%25253D; PPU=UID=116429460129353&UN=ambu4hugh&TT=0607959b2a1610a211b47a872eaa4729&PBODY=Ett-_jvIXly5Oi0Aif1CQiBpLeaKUCUXm8RfBBrq_bpq1kWUQ-8u7vA_xVzYPA0pXFzNPseUK9tSHsHn9nMG706LGcFknesfg5PVbvl1XHRGyi7i4GV1DCSMys-SVMz8YzwqW0_2Oj-fk6-bRYL5sOEFCOavBeKJMxEbdi-eZew&VER=1&CUID=Rai0Bv0vso1BsNZPMtzPww; fzq_h=30a4c67081e204b3bab466b5c1e857bc_1764252215942_a052a03fe3144d2fa748436a589c13c2_1867122615; JSESSIONID=918AD181FC8A99E665D3577E42C4D604; xxzlbbid=pfmbM3wxMDI5OXwxLjExLjB8MTc2NDI1MjIyMDI4NDY0MDk4MHx0OEorT3VOSzRncWN6ekViS3JJdXVUQU9NT1pacGlxVFZzaDlVN05kMVhrPXx8',
}
# 发送GET请求,获取目标页面的响应内容
res = requests.get(URL, headers=headers)
# 使用BeautifulSoup解析HTML内容(解析器为html.parser)
html = BeautifulSoup(res.content, 'html.parser')

# 定位页面元素(对应图片中的解析步骤)
title_des = html.find_all('span', class_="title_des")  # 商铺标题元素(class="title_des"的span标签)
withi = html.find_all('span', class_="withi")  # 区域/地址元素(class="withi"的span标签)
squa = html.find_all('p', class_='num')  # 商铺面积元素(class="num"的p标签)
price = html.find_all('span', class_='num')  # 租金元素(class="num"的span标签)

# 初始化列表,用于存储爬取到的数据
titles = []  # 存储商铺标题
area_address_list = []  # 临时存储区域+地址(后续拆分)
square = []  # 存储商铺面积
rent = []  # 存储租金
links = []  # 存储商铺详情页链接

# 提取商铺标题:遍历title_des元素,将文本存入titles列表
for ti in title_des:
    title = ti.text  # 获取标签内的纯文本
    titles.append(title)

# 提取区域/地址:遍历withi元素,将文本存入area_address_list列表
for wi in withi:
    addr = wi.text  # 获取区域/地址文本
    area_address_list.append(addr)

# 提取商铺面积:遍历squa元素,去除换行符后存入square列表
for sq in squa:
    squar = sq.text.replace('\n', '')  # 去除文本中的换行符
    square.append(squar)

# 提取租金并格式化:遍历price元素,添加"万/月"单位
for pr in price:
    pri = pr.text.replace('\n', '')  # 去除换行符
    # 正则匹配租金中的数字部分
    match = re.search(r'(\d+(\.\d+)?)', pri)
    if match:
        number = match.group() + '万/月'  # 给数字添加单位
        pri = pri.replace(match.group(), number)
    rent.append(pri)

# 打印基础数据,验证爬取结果
print(titles)
print(len(titles))
print(area_address_list)
# 将area_address_list拆分为areas(区域,索引为偶数)和addresses(地址,索引为奇数)
areas = [item for index, item in enumerate(area_address_list) if index % 2 == 0]
addresses = [item for index, item in enumerate(area_address_list) if index % 2 != 0]
print(areas)
print(len(areas))
print(addresses)
print(len(addresses))
print(square)
print(len(square))
print(rent)
print(len(rent))

# 提取商铺详情页链接(对应图片中的链接提取步骤)
li_list = html.find_all('li')  # 查找所有li标签(商铺条目容器)
for li in li_list:
    a_tag = li.find('a')  # 在li标签内查找a标签(详情页入口)
    if a_tag:
        link = a_tag['href']  # 获取a标签的href属性(详情页链接)
        links.append(link)
# 过滤链接:只保留长度≥200的有效详情页链接(排除无效短链接)
links = [link for link in links if len(link) >= 200]
print(links)
print(len(links))

# 初始化详情页数据存储列表
fee = []  # 存储转让费
names = []  # 存储联系人
tels = []  # 存储联系方式(二维码链接)

# 构造详情页的请求头(模拟浏览器访问详情页)
header = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67',
    'cookie': 'f=n; id58=CkwANGkoWfBebjKeCu4AAg==; wmda_uuid=46fcd9de7a42c1345c1c26763b2d9dc1; wmda_new_uuid=1; wmda_session_id_29317198571852=1764252144048-fe9da707-b2fb-48ad-befb-9c41593d4e8b; wmda_visited_projects=%3B29317198571852; wmda_session_id=1764252144130-5b68f4ab-d1e1-4565-8b26-ccee502f4d78; 58tj_uuid=380c661c-2aa7-43cc-8a86-83ddb6ecf9e1; new_uv=1; xxzlclientid=08c8d85b-4f4a-47fa-ac5c-1764252144462; als=0; xxzlxxid=pfmxiw0B2t+gimcGgzt4n/oWWVSAG6aKjfXw/wBnNbIJTuFSIPg7ioW+g38hUABHbEIC; utm_source=zhaopin_B; www58com="UserID=116429460129353&UserName=ambu4hugh"; 58cooper="userid=116429460129353&username=ambu4hugh"; 58uname=ambu4hugh; passportAccount="atype=0&bstate=0"; new_session=0; wmda_report_times=40; spm=u-2gn98rpfn988mb62t1.2gn99heh13vhwqfjjkg; 58home=nc; f=n; commontopbar_new_city_info=669%7C%E5%8D%97%E6%98%8C%7Cnc; city=nc; commontopbar_ipcity=nc%7C%E5%8D%97%E6%98%8C%7C0; init_refer=https%253A%252F%252Fwww.so.com%252Flink%253Fm%253DeOmptoAMqVCMapI8fmIxrYPH%25252BH89dOrTmgBYBzQkYG9Wh4AwymZD1uxCKbvBFj5VjHPw0J93oBsT6cyLck%25252FKOUUMOWgkf8lXl0nCb1WKx6p%25252BaAwGgkXhJ%25252Fw%25253D%25253D; PPU=UID=116429460129353&UN=ambu4hugh&TT=0607959b2a1610a211b47a872eaa4729&PBODY=Ett-_jvIXly5Oi0Aif1CQiBpLeaKUCUXm8RfBBrq_bpq1kWUQ-8u7vA_xVzYPA0pXFzNPseUK9tSHsHn9nMG706LGcFknesfg5PVbvl1XHRGyi7i4GV1DCSMys-SVMz8YzwqW0_2Oj-fk6-bRYL5sOEFCOavBeKJMxEbdi-eZew&VER=1&CUID=Rai0Bv0vso1BsNZPMtzPww; fzq_h=30a4c67081e204b3bab466b5c1e857bc_1764252215942_a052a03fe3144d2fa748436a589c13c2_1867122615; JSESSIONID=918AD181FC8A99E665D3577E42C4D604; xxzlbbid=pfmbM3wxMDI5OXwxLjExLjB8MTc2NDI1MjIyMDI4NDY0MDk4MHx0OEorT3VOSzRncWN6ekViS3JJdXVUQU9NT1pacGlxVFZzaDlVN05kMVhrPXx8'
}
# 遍历详情页链接,爬取转让费、联系人、联系方式
for lin in links:
    resp = requests.get(lin, headers=header)  # 发送请求获取详情页内容
    HTML = BeautifulSoup(resp.content, 'html.parser')  # 解析详情页HTML

    # 定位详情页元素
    house_basic = HTML.find_all('span', class_="house_basic_title_money_num_second")  # 转让费元素
    nam = HTML.find_all('span', class_="name-text")  # 联系人元素
    contact = HTML.find_all('img', class_="qr")  # 联系方式(二维码)元素

    # 提取联系人信息
    for na in nam:
        name = na.text
        names.append(name)
    new_names = names[1::2]  # 过滤重复联系人(取奇数位元素)

    # 提取二维码链接(联系方式)
    for con in contact:
        tel = con.get('src')  # 获取img标签的src属性(二维码图片链接)
        tels.append(tel)

    # 处理转让费信息(编码转换避免乱码)
    encoded_basic = house_basic[0].text.encode('utf-8')
    fe = []
    fe.append(encoded_basic.decode('utf-8').replace('\n', '').replace('\xa0', '').replace(' ', ''))
    fee.append(str(fe).encode('gbk', errors='ignore').decode('gbk'))
    print(str(fe).encode('gbk', errors='ignore').decode('gbk'))

# 清洗转让费数据:格式化显示
output = [elem.strip("['']").strip('"') for elem in fee]
out = []
for ele in output:
    if '转让费' in ele:
        if ele == '转让费:面议(转让费可面议)' or ele == '转让费:面议(费用可面议)':
            out.append('面议')
        else:
            ele = ele.replace('转让费:', '')
            ele = ele.replace('转让费:', '')
            out.append(ele)
    else:
        out.append('-')

# 打印详情页数据,验证结果
print(fee)
print(output)
print(out)
print(len(out))
print(new_names)  # 去重后的联系人列表
print(len(new_names))
print(tels)  # 联系方式(二维码链接)列表
print(len(tels))


#华丽转身的程序喵
#https://m.tb.cn/h.SxeNlci?tk=ubC5fNmxP5x

总结

本文通过 “请求→解析→提取→深入爬取” 的流程,实现了 58 同城商铺信息的批量获取。核心是用开发者工具定位元素 + BeautifulSoup 解析 HTML,掌握这个逻辑后,可轻松扩展到其他网站的爬虫开发~
如果对你有帮助,欢迎点赞 + 收藏!有问题可以在评论区交流~

注意事项

  • 反爬机制:频繁请求可能触发验证码,建议设置请求间隔(如time.sleep(2))。
  • 动态内容:若数据为动态加载(如JavaScript渲染),需改用Selenium或分析API接口。
  • 合法性:遵守网站robots.txt规则,避免高频访问。

通过以上步骤,可完整实现58同城商铺数据的批量爬取与存储。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐