爬取58同城二手手机

请注意,本文编写于 164 天前,最后修改于 151 天前,其中某些信息可能已经过时。

bs4爬取58同城二手手机

  1. 在开始编写代码前需要将Python3.7安装并配置于环境变量中(windows一般安装python环境后会自动添加进环境变量),以及使用pip命令安装上面提到的3个python库,这些都准备好以后开始使用PyCharm或者Sublime Text 3编写代码,这里我习惯于使用PyCharm编写python代码。
  2. 进入58同城的二手手机页面https://gy.58.com/shouji/ ,使用浏览器的开发者工具(直接按F12键即可)寻找页面规律。爬去58同城二手手机的数据。
  • 使用shift+ctrl+c选取页面标题元素,获取选中的url链接,查找页面规律
    点击标题后右边会跳转到对应的代码位置,通过点击多个列表得出结论,所有我们需要的url列表都在class为t的td标签下,而且对于的还有2种不同的地址,得到了页面规律后,我们就可以使用
  • 得到了上述的规律后,第一步我们需要先获取页面的html源代码,这里需要使用requests类,通过开发者工具的Network(网络),选中当前页面的Headers选项卡获取当前页面的HTTP请求头

获取http请求头以后使用python构造headers,并通过requests携带请求头headers访问https://gy.58.com/shouji/页面以及商品详情页面如https://hhpcpost.58.com/shouji/37346885041936x.shtm 等地址,这里我为了使用方便写了一个方法get_web_data()用于获取网页html代码

  • 获取url列表,通过上面的分析我们找到了url的规律,然后可以使用soup的select方法筛选元素,获取所有class为t的td标签下的a标签。示例如下
    urls = soup.select('td.t > a')

然后使用get()方法获取href属性,在获取链接的时候由于url有2种,并且页面布局完全不同,所以需要使用字符串分片的方式判断url链接的类型并且将2种url分为2个list存放,便于下一步的爬去

  • 获取页面数据标题、价格、描述信息、图片地址,由于58同城商品详情页面分为2种,需要分别为2种页面写不同的方法来获取页面信息。获取https://gy.58.com/shouji/37378994974604x.shtml 页面数据

同样通过开发者工具选取页面元素选取标题得到对应的位置, div.detail-info-hd > div.detail-info-tit并且使用strip()方法去除文本两边的空格以及换行符,使用同样的方法得到价格、区域以及描述信息。获取图片地址,在描述信息下方有商品的图片,使用开发者工具选取一张图片获得图片地址,寻找图片规律,所有图片在li标签下面的span标签中

  • 另一种页面的内容获取方式与上面的方法一致,只需要修改select方法选择对应元素。
  • 最后写一个main()方法遍历两个list中的地址分别访问对应的页面,并将获取到的数据存入MongoDb数据库

源代码

 from bs4 import BeautifulSoup
import requests
import pymongo
import re


# 封装requests,获取WEB页面数据
def get_web_data(url):
    # 构造请求头
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'zh-CN,zh;q=0.9',
        'cache-control': 'max-age=0',
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    data = requests.get(url, headers=headers)
    return data


# 通过页面爬去所有的详情页url
def get_urls(page_url):
    try:
        data = get_web_data(page_url)
        # 保存地址
        url_hhpcpost = []
        url_gy = []
        # 判断网页是否访问成功
        if data.status_code == 200:
            soup = BeautifulSoup(data.text, 'lxml')
            urls = soup.select('td.t > a')
            # urls = soup.select('div.ac_linkurl > a')
            for url in urls:
                # 获取a标签中的href属性
                url = url.get('href')
                # 判断url类型并且保存到列表中
                if url[2:10] == 'hhpcpost':
                    url_hhpcpost.append('https:' + url)
                if url[8:10] == 'gy':
                    url_gy.append(url)
        else:
            print(page_url + 'access failed!' + 'status_code:' + str(data.status_code))
    except Exception as e:
        print(e)
    finally:
        return url_gy, url_hhpcpost


# 获取二手商品信息
def get_shouji_info_gy(url):
    info = []
    try:
        # 判断网页内容是否获取成功若状态码为200则页面访问成功
        data = get_web_data(url)
        if data.status_code == 200:
            soup = BeautifulSoup(data.text, 'lxml')
            # 获取标题 并去除2边的换行符以及空白符
            title = soup.select('div.detail-title > h1')[0].get_text().strip()

            # 获取价格
            price = soup.select('div.infocard__container__item__main > span')[0].get_text().strip()

            # 使用正则表达式提取价格数字
            prices = re.search('\\d+', price)[0]

            # 区域
            areas = soup.select(
                'div.infocard__container.haveswitch > div:nth-of-type(2) > div.infocard__container__item__main>a')
            area = ''
            for i in areas:
                area = area + i.get_text().strip()

            # 描述信息
            description = soup.select('div.foldingbox > article.description_con')[0].get_text().strip()

            # 获取图片
            imgs = soup.select('li > span > img')
            pics = []
            for pic in imgs:
                pics.append('https:' + pic.get('src'))

            info = {
                'title': title,
                'prices': prices,
                'area': area,
                'description': description,
                'pics': pics
            }
    except Exception as e:
        print(e)
        print(url)
    finally:
        return info


# 获取二手商品信息
def get_shouji_info_hhpcpost(url):
    info = []
    try:
        # 判断网页内容是否获取成功若状态码为200则页面访问成功
        data = get_web_data(url)
        if data.status_code == 200:
            soup = BeautifulSoup(data.text, 'lxml')
            # 获取标题 并去除2边的换行符以及空白符
            title = soup.select('div.detail-info-hd > div.detail-info-tit')[0].get_text().strip()

            # 获取价格
            price = soup.select('div.detail-info-hd > div.detail-info-price > span.info-price-money')[
                0].get_text().strip()
            # 使用正则表达式提取价格数字
            prices = re.search('\\d+', price)[0]

            # 区域
            area = soup.select('ul > li:nth-of-type(3) > span.info-bd-text')[0].get_text().strip()

            # 描述信息
            description = soup.select('div.hh-detail-desc-box > div.hh-detail-desc')[0].get_text().strip()

            # 获取图片
            imgs = soup.select('div.hh-detail-small-pic > ul > li')
            pics = []
            for pic in imgs:
                pics.append('https:' + pic.get('data-src'))

            info = {
                'title': title,
                'prices': prices,
                'area': area,
                'description': description,
                'pics': pics
            }
    except Exception as e:
        print(e)
        print(url)
    finally:
        return info


def main():
    try:
        # 连接MongoDB数据库
        myclient = pymongo.MongoClient("mongodb://localhost:27017/")
        mydb = myclient['rs']
        collection = mydb['data_58']

        uri = 'https://gy.58.com/shouji/pn'
        for i in range(1, 29, 1):
            page_url = uri + str(i)
            (url_gy, url_hhpcpost) = get_urls(page_url)
            for url in url_gy:
                data = get_shouji_info_gy(url)
                # 保存数据到MongoDB
                if len(data) > 0:
                    collection.insert_one(data)
                print(data)
            for url in url_hhpcpost:
                data = get_shouji_info_hhpcpost(url)
                if len(data) > 0:
                    collection.insert_one(data)
                print(data)
    except Exception as e:
        print(e)


# print(get_shouji_info_gy('https://gy.58.com/shouji/37378994974604x.shtml?iuType=p_1&PGTID=0d300024-007d-f012-64a4-30fe82910e2d&ClickID=14'))
# print(get_shouji_info_hhpcpost('https://hhpcpost.58.com/shouji/37350593977988x.shtml?iuType=p_1&PGTID=0d300024-007d-f012-64a4-30fe82910e2d&ClickID=12'))
main()

运行结果