多进程爬取58同城二手市场

时间:2021-09-26 03:18:11

github网址:

https://github.com/factsbenchmarks/58-.git

58同城二手市场官网,以北京为例:http://bj.58.com/sale.shtml

通过查看前端代码,可以看出,每一类商品都是在 http://bj.58.com/ 后面加上相关英文字符。比如查找苹果手机,其url是 http://bj.58.com/iphonesj/

拼接的字符可以从前端查到。

<span>
                    <a href="/iphonesj/">苹果</a>
                    <a href="/sanxing/">三星</a>
                    <a href="/xiaomi/">小米</a>
                    <a href="/huaweisj/">华为</a>
                    <a href="/oppo/">OPPO</a>
                    <a href="/vivo/">VIVO</a>
                    <a href="/meizu/">魅族</a>
                    <a href="/kupaisj/">酷派</a>
                    <a href="/lianxiang/">联想</a>
                    <a href="/zhongxingshouji/">中兴</a>
                    <a href="/motuoluola/">摩托罗拉</a>
                    <a href="/nuojiya/">诺基亚</a>
                    <a href="/ailixin/">索尼</a>
                    <a href="/heimei/">黑莓</a>
                  <span>
                </span></span>

函数一:

  分析前端代码,爬取所有的二手商品的主页,保存到mongodb中。

import requests
from bs4 import BeautifulSoup
import time
import pymongo
client = pymongo.MongoClient('localhost',27017)
db = client['58city']
col = db['base_url']
base_url = 'http://bj.58.com/sale.shtml'
headers = {
        'Referer':'http://bj.58.com/',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
}

def get_type_base_url(url):
    '''
    爬取每类商品的主页,存储在mongodb中。
    :param url: 58同城首页
    :return: None
    '''
    r = requests.get(url, headers=headers)
    soup = BeautifulSoup(r.text, 'lxml')
    item_lists = soup.select('ul.ym-submnu > li > span > a')
    for item in item_lists:
        item_type_base_url = 'http://bj.58.com' + item.get('href')
        item_type_name = item.get_text()
        col.insert_one({'item_type_name': item_type_name,'item_type_base_url':item_type_base_url})  # 插入字典格式

get_type_base_url(base_url)

  mongodb中存储的数据如下:

  多进程爬取58同城二手市场

函数二:获取某类商品某个页码中的所有商品的的url。

  每一类商品,都有好几页来显示,有的甚至有数十页码。

  比如。http://bj.58.com/iphonesj/pn2/,区分之处在于每一类商品后加 pn,pn后的数字表示页码数。

  在每页中,都有数十个商品。

  这个函数需要的参数是:某类商品的主页,某个page页码。

import requests
from bs4 import BeautifulSoup
import pymongo
import hashlib
flag = '很抱歉,没有找到相关信息'
client = pymongo.MongoClient('localhost',27017)
db = client['58city']
col = db['base_url']
col_detail_url = db['detail_url']
col_detail_info = db['detail_info']

headers = {
        'Referer':'http://bj.58.com/',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
}

def get_detail_url(base_url,page):
    '''
    传入base_url,传入某个页码,返回当前这个页码的所有的detail的url,并保存到mongodb中。
    :param base_url:
    :param page:
    :return:
    '''
    url = base_url + 'pn{}'.format(page)
    r = requests.get(url)
    if flag in r.text:   # 某类商品的某页,可能不存在。判断,58同城服务器有没有这个页面。
        pass
    else:
        soup = BeautifulSoup(r.text,'lxml')
        detail_as = soup.select('td.t > a')
        for item in detail_as:
            detail_url = item.get('href')
            if detail_url.startswith('http://zhuanzhuan.58.com'):  # 排除干扰项
                col_detail_url.insert_one({'detail_url':detail_url})

 

函数三 :拿到具体某个商品的url,爬取想要的信息。比如,价格,区域,标题,描述等等

    这个函数需要的参数,某个商品的url。

import requests
from bs4 import BeautifulSoup
import pymongo
import hashlib
flag = '很抱歉,没有找到相关信息'
client = pymongo.MongoClient('localhost',27017)
db = client['58city']
col = db['base_url']
col_detail_url = db['detail_url']
col_detail_info = db['detail_info']

headers = {
        'Referer':'http://bj.58.com/',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
}

def get_detail_info(url):
    '''
    爬取某个商品的想象信息,并保存到mongodb中,
    :param url:
    :return:
    '''
    r = requests.get(url, headers=headers)
    soup = BeautifulSoup(r.text, 'lxml')
    title = soup.find('h1', class_='info_titile').get_text() if soup.find('h1', class_='info_titile') else None
    price = soup.select('span.price_now > i')[0].get_text() if soup.select('span.price_now > i') else None
    zone = soup.select('div.palce_li > span > i')[0].get_text() if soup.select('div.palce_li > span > i') else None
    desc = soup.select('div.baby_kuang > p')[0].get_text() if soup.select('div.baby_kuang > p') else None
    detailurl = url
    if title or price or zone or desc:
        col_detail_info.insert_one({'title': title, 'price': price, 'zone': zone, 'desc': desc, 'detailurl': detailurl})

 

函数四:利用多进程加快爬取。

  以上三个函数,该有的都有了。为了更快速的爬取,需要用到多进程Pool的map方法。

  注意,函数二  get_detail_url ,仅仅只是爬取到了某类商品某一页 显示的所有的商品的url,而这很明显是不够的。我们需要爬取的是所有类商品的,很多页的商品的详细信息。

  所有商品,其主页url,函数一已经拿到了。很多页,多少页,可以自己确定。想爬20页就20页,想100页就100页。

  

  map方法的官方说明:

    def map(self, func, iterable, chunksize=None):
        '''
        Apply `func` to each element in `iterable`, collecting the results
        in a list that is returned.
        '''
        return self._map_async(func, iterable, mapstar, chunksize).get()

  第一个参数是一个函数,第二个参数是一个可迭代对象。基于此,我们构建一个函数和一个可迭代对象。

  这个函数是爬取某类商品的全部前N页,这个可迭代对象是将函数一get_type_base_url爬下来的 所有类商品的主页,构成的列表。

from multiprocessing import Pool
import os
from get_itembaseurl import get_type_base_url
from parse import get_detail_info,get_detail_url
client = pymongo.MongoClient('localhost',27017)
db = client['58city']
col = db['base_url']
base_urls = []
for item in col.find():
    base_urls.append(item['item_type_base_url'])


def get_all_detail_url(base_url):
    '''
    调用get_detail_url函数,爬取某种商品的的前N页所有商品的url。
    :param base_url: 
    :return: 
    '''
    for i in range(1,10):
        get_detail_url(base_url,i)

if __name__ == '__main__':
    p = Pool()
    p.map(get_all_detail_url,base_urls)
    p.close()
    p.join()

 

函数五

  定义一个函数,实时监测爬取的保存到mongodb中的的数据的数量。

import pymongo
import time
client = pymongo.MongoClient('localhost',27017)
db = client['58city']
col = db['detail_url']
while True:
    print(col.find().count())
    time.sleep(5)