简书日活用户至少几十万,因此抓取简书的用户,一方面做数据分析和挖掘,另一方面,看看有哪些it同行,挖掘一些高质量的文章。
本文先分析抓取数据,结合抓取策略和scrapy框架,一步一步教你带你做爬虫和挖掘分析。
先说抓取思路。本文思路是选择一个大V,大v就是关注用户较多,粉丝也多的用户作为爬虫抓取入口,然后不断对其关注用户和粉丝用户抓取,再对关注用户和粉丝的关注用户和粉丝进行抓取,如此循环。
抓取函数入口:
def start_requests(self): start_url = 'https://www.jianshu.com/u/811ae6268caa' yield Request(start_url, callback=self.parse)
parse函数式scrapy的回调函数,主要解析用户信息和用户发表的文章,同时解析出其关注列表和粉丝列表进行递归抓取。当然抓取列表显然是有多页的,而这在简书里面的体现在ajax异步加载,只在拖动下拉
的时候才会触发请求数据。这里拿到分页列表后,采取url拼接的方式构造分页请求。
followed_url = 'https://www.jianshu.com'+info_selectors[0].xpath("./div/a/@href").extract()[0] followed = info_selectors[0].xpath("./div/a/p/text()").extract()[0] pages = int(float(followed)/10) for page in range(1,pages+1): userlist_url = followed_url + '?page={page}'.format(page=page) yield Request(userlist_url, callback=self.parseuserlist, dont_filter=True)
同理抓取粉丝列表。抓取用户文章也一样。
articles_url = 'https://www.jianshu.com' + info_selectors[2].xpath("./div/a/@href").extract()[0] articles = info_selectors[2].xpath("./div/a/p/text()").extract()[0] #抓取文章 article_page= int(float(articles)/10) for page in range(1,article_page+1): articellist_url = articles_url + '?order_by=shared_at&page={page}'.format(page=page) yield Request(articellist_url, callback=self.parse_article_list, dont_filter=True)
这样既可抓取文章了。
再看parseuserlist和parse_article_list。解析用户信息和文章信息,然后持久化到文件。
def parseuserlist(self,response): url_list = response.xpath("//ul[@class='user-list']/li/div[@class='info']/a/@href").extract() url_list = ['https://www.jianshu.com'+url for url in url_list] for url in url_list: yield Request(url,callback=self.parse,dont_filter=True)
def parse_article_list(self, response): """ 根据总文章数抓取的每一页的文章列表 :param response: """ base_url_p = 'https://www.jianshu.com/p/' all_links_p = [base_url_p + link for link in re.compile('href="/p/(.*?)"', re.S).findall(response.text)] all_article_links = set(all_links_p) for article_link in all_article_links: yield Request(url=article_link, callback=self.parse_article, dont_filter=True)
抓取文章列表的时候对每篇文章进行解析,parse_article函数负责解析。
def parse_article(self, response): try: bs = BeautifulSoup(response.text, 'lxml') url = response.url article = ArticleItem() article['id'] = str(url).split('/')[-1].split('?')[0] article['url'] = url article['title']=bs.select("title")[0].get_text() # 标题 article['name']=bs.select("div[class='author']")[0].select("span[class='name']")[0].get_text() # 作者名字 article['post_time']=bs.select("span[class='publish-time']")[0].get_text() # 发布时间 article['content']=bs.select("div[class='show-content']")[0].get_text() # 文章内容 # article['read_count']= bs.select("span[class='views-count']")[0].get_text() if bs.select("span[class='publish-time']") else "0" # 阅读次数 # article['comment_count']=bs.select("span[class='comments-count']")[0].get_text() if bs.select("span[class='comments-count']") else "0" # 评论次数 # article['love_count']=bs.select("span[class='likes-count']")[0].get_text() if bs.select("span[class='likes-count']") else "0" # 喜欢次数 yield article except Exception as e: print(e)
持久化pipeline,这里为了简便直接写入文件。
def process_item(self, item, spider): if isinstance(item, JianshuItem): with open('jianshu_user.csv', 'a+', encoding='utf-8', newline='') as f: writer = csv.writer(f) writer.writerow((item['id'], item['url'], item['nickname'], item['description'], item['followed'], item['following'], item['articles'], item['charlength'], item['likes'])) return item elif isinstance(item, ArticleItem): with open('jianshu_article.csv', 'a+', encoding='utf-8', newline='') as f: writer1 = csv.writer(f) writer1.writerow( (item['id'], item['url'], item['name'], item['title'], item['post_time'], item['content'])) return item
这里要保存的数据分为user和article两个类。保存的数据在item类体现。
class JianshuItem(Item): url = Field() #主页 id = Field() nickname = Field() description = Field() followed = Field() following = Field() articles = Field() charlength = Field() likes = Field() class ArticleItem(Item): url = Field() #当前网页的url id = Field() #作者ID name = Field() # 作者名字 title = Field() #标题 post_time = Field() #发布时间 content = Field() #文章内容 read_count = Field() #阅读次数 comment_count = Field()#评论次数 love_count = Field() #喜欢次数
最后,为了反爬虫,增加模拟浏览器的请求头,在scrapy的增加HeadersDownloaderMiddleware中间件,并在配置文件开启。
class HeadersDownloaderMiddleware(UserAgentMiddleware): def process_request(self, request, spider): ua = random.choice(UserAgentList) request.headers.setdefault('User-Agent:', ua)
其中
UserAgentList为手动配置的,当然也可以使用fake—Agent。
最后,在命令行执行:
sys.path.append(os.path.dirname(os.path.basename(__file__))) execute(['scrapy','crawl','jianshu'])
爬虫就跑起来了。