解放号主页url:https://www.jfh.com/
进入到解放号主页我们可以看到这样的界面,这次我们要爬取的是软件定制开发这个大类下面的所有小类的公司信息
我们点击服务商库下面的软件定制开发
可以发现我们要爬取的所有公司都在这里面,右键检查,我们可以看到所有的公司信息都放在一个带有class='datajp_shopLogo'的div标签下面。正常套路是我们查找这个div标签下的所有div元素,然后获取每个公司的url就能够得到公司的信息了。可是当我使用xpath对返回回来的页面进行获取信息时,发现返回的永远是一个空的列表。沿着爬取猪八戒网的思路,我们右键检查网页源代码,随便复制一个公司信息检查
我们发现这个网页的源代码里没有我们想要的信息,因此我们就知道它的信息肯定是写在某个js文件里面
右键查找,点击network 按F5后在search处输入一个公司的名字,我们就能发现这个名为queryShopShow的js文件
按照爬取猪八戒网的思路,我们只要请求下面的request url,返回回来的数据应该就是我们想要的数据。
注意到这个是post请求,按照以往的经验下面会有一个数据头,我们在使用request.post请求的时候一定要加下面的form_data
可是当我请求reques url的时候我发现它返回给我的数据什么都没有,按照爬取猪八戒的方法行不通。我把reque url放进百度里发现它是一个登陆页面,就像下图所示。我一开始以为要登陆之后才会返回给我数据,一开始解放号这个注册页面有BUG,我打电话给他们前台告知他们这个情况后,在第二天的下午就解决了。效率很高。
在网站我还想着使用CookieJar模块模拟登陆才能得到我要的信息,后来注册登陆进去以后发现它还是公司的主页,如下图 顿时我就怀疑人生了。这怎么会回到原来的地方?这不是无限死循环了吗?
后来检查一下使用post返回的页面,发现里面有信息,但是是json格式的。我们把结果复制到json.cn网站中在线解析一下
发现所有公司的信息都写在shopList里面。可是我能得到的公司信息就这些了吗?这些还远远不够。我突然发现里面有个shopId,我就想这个id是不是代表着一个公司的url,结果发现真的是。那我们接下来就有思路了,获取到每页的json数据后,我们再获取每一个公司的shopId,然后就能得到公司的主页url了。我们想要的是公司的服务商档案url,发现只要在主页的基础上加上'/bu/'就行了。
代码如下:
def get_company_shopid(url,x):
result_json = get_page_json(url,x)
Ids = len(result_json['shopList'])
shopid_list = []
for id in range(Ids):
shopid_list.append(result_json['shopList'][id]['shopId'])
return shopid_list
def get_page_json(url,x):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'Referer': 'https://list.jfh.com/shops/m01?webSign=&keyWords=',
'X-Requested-With': 'XMLHttpRequest', # 这个很重要
'Host': 'list.jfh.com',
'Origin': 'https://list.jfh.com',
'Pragma': 'no-cache'
}
DATA = {
'page':x,
'pageSize': 20,
'category': 5,
'orderType': 1,
'keyWords': '',
'webSign': '',
'mcode': 'm01'
}
resp = requests.post(url,headers=headers,data=DATA)
resp.encoding = 'utf-8'
return resp.json()
base_url = 'http://list.jfh.com/shop/serviceProvider/queryShopShow'
for i in range(1,283):
shopid_list = get_company_shopid(base_url,i)
for shop_id in shopid_list:
company_url = 'https://shop.jfh.com/' + str(shop_id) + '/bu/'
获取完每个公司的url之后,同样的我们要在爬取这个公司之前判断之前是否已经被爬取过。还要开多线程提高爬取速度,完整代码如下:
import requests
from lxml import etree
import threading
from queue import Queue
import json
import pymysql
def getHTMLText(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER',
'Host':'shop.jfh.com'
}
resp = requests.get(url,headers=headers)
resp.encoding = 'utf-8'
return resp.text
def get_page_json(url,x):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'Referer': 'https://list.jfh.com/shops/m01?webSign=&keyWords=',
'X-Requested-With': 'XMLHttpRequest', # 这个很重要
'Host': 'list.jfh.com',
'Origin': 'https://list.jfh.com',
'Pragma': 'no-cache'
}
DATA = {
'page':x,
'pageSize': 20,
'category': 5,
'orderType': 1,
'keyWords': '',
'webSign': '',
'mcode': 'm01'
}
resp = requests.post(url,headers=headers,data=DATA)
resp.encoding = 'utf-8'
return resp.json()
def get_company_shopid(url,x):
result_json = get_page_json(url,x)
Ids = len(result_json['shopList'])
shopid_list = []
for id in range(Ids):
shopid_list.append(result_json['shopList'][id]['shopId'])
return shopid_list
class Consummer(threading.Thread):
def __init__(self,company_queue,*args,**kwargs):
super(Consummer,self).__init__(*args,**kwargs)
self.company_queue = company_queue
def run(self):
while True:
if self.company_queue.empty():
break
company_url = self.company_queue.get()
self.get_and_write_company_info(company_url)
print(company_url + '写入完成')
def get_and_write_company_info(self,url):
conn = pymysql.connect(host=****, user=****, password=****, database=****,port=****, charset='utf8')
cursor = conn.cursor() # 连接数据库放在线程主函数中的,如果放在函数外面,就会导致无法连接数据库
company_url = url
text = getHTMLText(url)
html = etree.HTML(text)
company_name = html.xpath("//p[@id='company_name']//text()")[0]
spans = html.xpath("//p[@id='company_achievement']//span")
trade_nums = spans[0].xpath(".//text()")[0]
trade_money = int(spans[1].xpath(".//text()")[0].split("¥")[1])
good_comment_rate = int(spans[2].xpath(".//text()")[0].split("%")[0])
company_info = html.xpath("//div[@class='companyProfile']/p/text()")[0].strip().replace("\n", '')
spans = html.xpath("//span[@class='reset-style']")
if len(spans) >= 4:
try:
work_years = int(spans[0].xpath(".//text()")[0].split(":")[1].split("年")[0])
except:
work_years = 0
try:
peoples = int(spans[1].xpath(".//text()")[0].split("名")[0])
except:
try: #有些人数是未填写,有些是 类似2,500 9,200这种的
peoples = ''
strings = spans[1].xpath(".//text()")[0].replace('\\n', '').strip().split(',')
for string in strings:
peoples += string
peoples = int(peoples)
except:
peoples = 0 #0代表未填写
build_time = spans[2].xpath(".//text()")[0].split("成立")[0].strip().replace("\n", '')
try:
address = spans[3].xpath(".//text()")[0].strip().replace("\n", '')
except:address='暂无'
else:
build_time = '暂无'
try:
work_years = int(spans[0].xpath(".//text()")[0].split(":")[1].split("年")[0])
except:
work_years = 0
try:
peoples = int(spans[1].xpath(".//text()")[0].split("名")[0])
except:
try: #有些人数是未填写,有些是 类似2,500 9,200这种的
peoples = ''
strings = spans[1].xpath(".//text()")[0].replace('\\n', '').strip().split(',')
for string in strings:
peoples += string
peoples = int(peoples)
except:
peoples = 0 # 0代表未填写
try:
address = spans[2].xpath(".//text()")[0].strip().replace("\n", '')
except:address='暂无'
spans = html.xpath("//div[@class='beGoodAt clearfix']/div//text()")
good_at_skill = []
for span in spans:
if len(span.strip()) >= 3:
good_at_skill.append(span)
good_at_skill = json.dumps(good_at_skill, ensure_ascii=False)
sql = """
insert into ****(id,company_name, company_url, trade_nums,trade_money,good_comment_rate,peoples, company_info, good_at_skill,build_time, address,work_years) values(null,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
"""
cursor.execute(sql, (
company_name, company_url, trade_nums, trade_money, good_comment_rate, peoples, company_info, good_at_skill,
build_time, address, work_years))
conn.commit()
def main():
company_queue = Queue(10000)
is_exists_company = []
company_nums = 0
base_url = 'http://list.jfh.com/shop/serviceProvider/queryShopShow'
for i in range(1,283):
shopid_list = get_company_shopid(base_url,i)
for shop_id in shopid_list:
company_url = 'https://shop.jfh.com/' + str(shop_id) + '/bu/'
if company_url in is_exists_company:
continue
else:
is_exists_company.append(company_url)
company_nums += 1
company_queue.put(company_url)
print('第{}页写入完成,已写入{}个'.format(i,company_nums))
print('公司url收集完毕,开启多线程')
for x in range(5):
t = Consummer(company_queue)
t.start()
if __name__ == '__main__':
main()