<strong>之前的文章都是与职位、热门文章有关的,今天一起来看一下知乎上与python相关的精华回答(主要是requests,scrapy处理的思路,编码问题)</strong>
1.采集的信息
<ol>
<li>问题题目</li><li>问题对应URL</li><li>回答的作者</li><li>作者主页URL</li><li>回答获赞数</li><li>回答详情URL</li></ol><strong>主要获取以上信息,接着我们来看一下这些字段所在的位置
通过观察和分析以上字段在源码中都能找到,所以就不用像前几篇文章那样,构造参数,解析获取返回的json数据,我们可以通过lxml.etree.HTML()或者Beautifulsoup去解析(这里在通过etree解析时遇到了一些问题,在后边会进行解释)
既然不需要通过构造参数去获取数据了,所以相对来说就简单一些了,接下来我们来看一下另一个很重要的问题——分页问题</strong>
<em>从上面两张图片我们可以看出换页之后参数<strong>page</strong>发生了改变,很显然<strong>page</strong>控制的是页码,明白了这一点就可以去构造URL了</em>
2.构造URL
urls = ['https://www.zhihu.com/topic/19552832/top-answers?page={}'.format(one) for one in range(1,51)]
<strong>解决了分页问题,就没什么难点了,接下来说一下遇到的问题和解决方法
- 通过Beautifulsoup解析页面
# -*- coding:utf-8 -*-
import requests
from bs4 import BeautifulSoup
from store_csv import CSV
from store_mysql import Mysql
class zhiHu(object):
baseurl = 'https://www.zhihu.com'
headers = {
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
}
cookies = {
'd_c0': '"AHACIj6lFguPTkEZTVmz8MPJ8UkcuI03hag=|1483278273"',
'_zap': '108369f2-93e6-407e-8928-b57f44168838',
'q_c1': '0d3d690c67bc4b12a163beb462e17d67|1492571575000|1483278273000',
'cap_id': '"NmFlNDgyZmMxNWQ0NGFiYmFhZThmMGM2MDVkZDM1MWM=|1493684435|1958b2bb9fb6f09116056cc4bc5af76e00dbaff7"',
'l_cap_id': '"ZjViYmQ0MzBkNTFjNGJjOWE5YTExZWRjMjlkNjM2YjQ=|1493684435|1204e5c054805d8bebf3b49a8a2fc09e2f1bd870"',
'auth_type': '"c2luYQ==|1493684515|245a0f575f151c486829c687ba3d3c90fc5fa558"',
'token': '"Mi4wMGljWjNYRkVBNzIyRGJjZDZkMjMyZmMwM2JtMWs=|1493684515|a76491c048b7e825a77983ef0a272bdc9e721853"',
'client_i': '"NTA3MzY2MzQwNA==|1493684515|9a08a2c9a6c02e86da4810cb5bc4437f6d7b1028"',
'aliyungf_tc': 'AQAAAFOsE04F5QQAHVcc2i2qHTGdX1x0',
'acw_tc': 'AQAAAAnnBnQS0AcAHVcc2omDblHld+wt',
'_xsrf': '398e0b041f39355dadfc06b76ba18cdb',
's-q': 'python',
's-i': '1',
'sid': 'ob2ttqkg',
's-t': 'autocomplete',
'z_c0': 'Mi4wQUFBQ3hmbTFzUXNBY0FJaVBxVVdDeGNBQUFCaEFsVk5OVjR2V1FETW9QS19PRlExWGVjUHNOcFVoTG5mYWg1LUtR|1493701061|4258e8f2ddc800c42c8b7d94385f578ca97f34d5',
'__utma': '51854390.213172690.1484833633.1493684449.1493701029.4',
'__utmb': '51854390.0.10.1493701029',
'__utmc': '51854390',
'__utmz': '51854390.1493701029.4.4.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
'__utmv': '51854390.100--|2=registration_date=20170502=1^3=entry_date=20170101=1'
}
def getUrl(self):
urls = ['https://www.zhihu.com/topic/19552832/top-answers?page={}'.format(one) for one in range(1,51)]
for url in urls:
self.getData(url)
def getData(self,url):
data = requests.get(url, headers=self.headers, cookies=self.cookies).content.encode('utf-8')
soup = BeautifulSoup(data, 'lxml')
infos = soup.find_all('div', {'class': 'feed-item feed-item-hook folding'})
item = {}
for info in infos:
item[1] = info.find('a',{'class':'question_link'}).get_text().strip()
item[1] = item[1].replace("'","=")
item[2] = self.baseurl+info.find('a',{'class':'question_link'}).get('href')
try:
item[3] = info.find('a',{'class':'author-link'}).get_text()
except:
item[3] = u'匿名用户'
try:
item[4] = self.baseurl+info.find('a',{'class':'author-link'}).get('href')
except:
item[4] = u''
item[5] = info.find('a',{'class':'zm-item-vote-count js-expand js-vote-count'}).get_text()
try:
item[6] = self.baseurl+info.find('a',{'class':'toggle-expand'}).get('href')
except:
item[6] = u''
self.store(item)
def store(self,item={}):
#row = [item[i] for i in range(1,7)]
#wirte.writeRow(row)
mysql.insert(item)
if __name__ == "__main__":
#firstRow = ['问题', '问题URL', '回答者', '回答者URL', '获赞', '详细信息URL']
#wirte = CSV('zhihu.csv',firstRow)
row = ['question','qurl','autuor','aurl','zan','detail']
mysql = Mysql('zhihu',row,7)
mysql.create_table()
zhihu = zhiHu()
zhihu.getUrl()
</strong><em>这里说明一点,from store_csv import CSV,from store_mysql import Mysql这两个是自己写的两个类,实现方法可以参考python简单操作csv文件小例和老哥Mr_Cxy的python对Mysql数据库的操作小例这两篇文章,这里可以不添加cookie信息</em>
<strong>
- 通过etree解析页面(和之前一样都是找到循环点,然后取值,这里主要说一下遇到的问题和解决的方法)
- 问题</strong>
html = requests.get(url,headers=headers)
这里有不同方式,一种是html.content,另一种是html.text之前没遇到出错的情况(通过content出现了编码问题,python版本2.7),今天遇到了就记录一下:
resp.text返回的是Unicode型的数据。
resp.content返回的是bytes型也就是二进制的数据。
也就是说,如果你想取文本,可以通过r.text。
如果想取图片,文件,则可以通过r.content。 - <strong>方法</strong>
主要说一下html.content的解决方案
selector = etree.HTML(html.content.decode('utf-8'))
通过对html.content进行解码,输出结果正常。
3.scrapy版本思路
- <strong>1.使用start_requests()重写初始URL控制或者在parse函数中进行分页的控制
yield scrapy.Request(url,callback=self.parse)
</strong><em>代码中提到的URL指的是下一页的URL,通过获取下一页信息调用parse函数抓取信息</em><strong>
- 2.之前通过观察URL,我们发现只有page之后的数字发生了变化,所以我们可以使用爬取规则CrawlSpider去获取不同页面的信息(给一个之前抓取智联招聘职位详情页面的规则作为参考)
# -*- coding: utf-8 -*- from scrapy.spider import CrawlSpider,Rule from scrapy.linkextractors import LinkExtractor from ..items import ZlzpItem class ZLZP_Spider(CrawlSpider): name = 'ZLZP' start_urls = [ 'http://jobs.zhaopin.com/' ] rules = [ Rule(LinkExtractor(allow=('http://jobs.zhaopin.com/[a-zA-Z0-9]*\.htm',)), follow=True, callback='parse_item')] def parse_item(self, response):
这里使用了parse_item进行页面数据的提取(编写爬虫规则时,避免使用 parse 作为回调函数。 由于 CrawlSpider
使用 parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider 将会运行失败。)
关于CrawlSpider
的详细使用方法可以参考官方文档或者参考相关的代码和文章
4.数据信息(mysql和csv)
总结
通过对比动态网页(ajax)和静态网页,在思想上差别不是很大(参数+分页),在解析方式动态的一般都是通过解析json来获取数据,静态则通过Beautifulsoup、xpath、正则去获取数据。
爬爬爬,下一个网站走起!