在前面几篇博文里其实已经介绍了和Python爬虫相关的很多基础知识,包括基本的抓取网页信息,ip池的建立和使用,多进程在ip验证中的使用,今天我们就把这些内容整合到一起,完成一个真正的爬虫。
我们先来梳理一下思路,要爬取所有职位的详细信息,应该包括以下几步:
1 爬取所有职位及其对应的url
2 对每一个职位所对应的url进行信息提取
3 将信息保存在本地数控库中
4 如果要防止ip被封还需要去爬取可用的ip并验证
那现在就来一步步完成以上这些工作。首先要说的是,最近真的很迷requests这个库,感觉比urllib简洁太多了,用着就两个字,酥胡!所以爬取所有职位对应的url代码如下:
#爬取职位及其对应url
def getLink(url,proxy=''):
response=requests.get(url,proxies=proxy)
response.encoding='gbk'
bs=BeautifulSoup(response.text)
res=bs.find_all("td",{"class":"item1"})
links=[]
for item in res:
if 'http://my.yingjiesheng.com/job' in item.find('a').attrs['href']:
links.append(item.find('a').attrs['href'])
else:
pass
return links
这里getLink函数添加一个proxy变量主要是为了后面如果要用ip池的话方便改代理。然后为什么在选择网址时加了一个筛选条件呢,主要是因为应届生网站职位的详细信息页面分为两种,如果来源是应届生网,如下
可以看到职位信息非常的规整,很方便地可以进行信息提取。而如果来源是一些其他的网站
基本就是一大段描述,而且一家和一家不一样,所以这里在爬取职位详细信息时,只针对那些来源是应届生网的网页。
然后第二步就是信息提取了,这对大家来说都不是什么问题了,就是利用BeautifulSoup来提取网页信息,网页源码如下
可以看到,公司的信息都在<div class="main">中,而职位的详细信息都在<div class="section">中,结合标签所处的位置我们可以完成信息提取,实现代码如下:
def getInfo(url,proxy=''):
info={u'公司名称':'',u'所属行业':'',u'企业规模':'',u'企业性质':'',u'职位名称':'',u'工作地点':'',u'有效日期':'',u'招聘人数':'',u'职位性质':'',u'职位描述':''}
response=requests.get(url,proxies=proxy)
response.encoding='gbk'
bs=BeautifulSoup(response.text)
info[u'公司名称']=bs.find('div',{'class':'main'}).find('h1').text
temp=[]
for item in bs.find('div',{'class':'main'}).find('ul').find_all('li'):
temp.append(item.find('span').text)
info[u'所属行业']=temp[0]
info[u'企业规模']=temp[1]
info[u'企业性质']=temp[2]
info[u'职位名称']=bs.find('div',{'class':'main'}).find('h2').text
temp=[]
for item in bs.find('div',{'class':'main'}).find('div',{'class':'job_list'}).find('ul').find_all('li'):
try:
temp.append(item.find('span').text)
except:
temp.append(item.text)
info[u'工作地点']=temp[0]
info[u'有效日期']=temp[1]
info[u'招聘人数']=temp[2]
info[u'职位性质']=re.sub(u'\u804c\u4f4d\u6027\u8d28\uff1a','',temp[3])
describe=bs.find('div',{'class':'main'}).find('div',{'class':'j_i'}).text
pattern=pattern=re.compile('\t|\r\n')
describe=re.sub(pattern,'',describe)
info[u'职位描述']=describe
doc.insert_one(info)
然后第三步是保存到本地的数据库中,这里使用的是MongoDB,结合pymongo库几步就可以实现。然后ip池的代码之前也说过了,略有一些小调整如下:
#爬取代理ip
def getIp(numpage):
csvfile = file('ips.csv', 'ab')
writer = csv.writer(csvfile)
url='http://www.xicidaili.com/nn/'
user_agent='IP'
headers={'User-agent':user_agent}
for i in xrange(1,numpage+1):
real_url=url+str(i)
response=requests.get(real_url,headers=headers)
content=response.text
bs=BeautifulSoup(content)
trs=bs.find_all('tr')
for items in trs:
tds=items.find_all('td')
temp=[]
try:
temp.append(tds[1].text)
temp.append(tds[2].text)
writer.writerow(temp)
except:
pass
#从本地获取ip
def getProxy():
reader=csv.reader(open('ips.csv'))
Proxy=[]
for row in reader:
proxy={"http":row[0]+':'+row[1]}
Proxy.append(proxy)
return Proxy
#ip测试
def Test(proxy):
try:
response=requests.get('http://www.yingjiesheng.com/',proxies=proxy,timeout=2)
if response:
return proxy
except:
pass
这样的话所需要的功能都有了稍微组织一下就能实现我们最终的功能了
if __name__=='__main__':
time1=time.time()
connection=pymongo.MongoClient()
db=connection.YingJieSheng
doc=db.info
getIp(10)
print 'Have got ips , now testing'
proxy=getProxy()
pool=multiprocessing.Pool(processes=16)
IPPool=[]
temp=[]
for item in proxy:
temp.append(pool.apply_async(Test,(item,)))
pool.close()
pool.join()
for item in temp:
IPPool.append(item.get())
print 'Effective ips have been selected'
url_head='http://www.yingjiesheng.com/beijing-morejob-'
url_end='.html'
for i in xrange(1,361):
links=[]
ipproxy=IPPool[i%len(IPPool)-1]
try:
links=getLink(url_head+str(i)+url_end,ipproxy)
except:
links=getLink(url_head+str(i)+url_end)
for link in links:
try:
getInfo(link,ipproxy)
except:
try:
getInfo(link)
except:
pass
print 'Downloading the '+str(i)+' page'
time2=time.time()
print 'Total time is '+str(time2-time1)+' s'
最后运行的结果如下所示:
这样就完成了我们第一个完整的爬虫,爬取应届生网上所有职位的详细信息。当然实际操作过程中还是有一点点小问题,那就是很多爬取到ip代理不太好用,导致最后很多网页还是以本地ip去爬的,访问频率过高之后依然会导致ip被封的问题。所以如果不是很着急的话,还是在代码里加上sleep,不要对网站的访问过于密集了,以免造成服务器不必要的负担,我觉得这算是爬虫比较基本的素养吧。
最后,祝大家新年快乐~新的一年里,希望可以继续朝着自己心里的方向前进,加油!!!