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中存储的数据如下:
函数二:获取某类商品某个页码中的所有商品的的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)