刚过年,又要到了一年一季的毕业季,马上就要到了大四学长学姐们提交毕业论文的时节,这次爬《汽车之家》的文章就是帮一位学长准备毕业论文研究资料。汽车之家的反爬虫措施做得很好,用了字体反爬技术。对于这类的反爬技术,我上次在帮另一位学长爬《大众点评》的时候也遇见过,当时并没有认真研究是怎样对付这类技术的,现在又遇见了,所以说“学习的苦,一定要吃”。为了学习《汽车之家》的反爬技术,我几乎参考完了所有关于它的博客,最终完成了技能学习。

文章目录

  • 1、汽车之家论坛
    • 1.1、分析网页构造
    • 1.2、获取网页源代码
    • 1.3、用户随机代理
    • 1.4、字体替换
    • 1.5、爬取论坛链接主题链接
      • 1.5.1、构造论坛首页翻页链接
      • 1.5.2、爬取论点链接
    • 1.6、实现评论内容翻页
    • 1.7、大功告成,附上源码
  • 2、汽车之家问答
  • 3、汽车之家新闻
    • 3.1、新闻内容
    • 3.1、新闻评论
  • 4、汽车之家文章
    • 4.1、爬取所有文章的链接
    • 4.2、判断新闻和车家号
    • 4.3、车家号文章
    • 4.4、代码汇总
  • 5、结果汇总截图

1、汽车之家论坛

  • 这里以比亚迪新能源汽车作为爬取对象

1.1、分析网页构造

  • 我用的是谷歌浏览器,打开网页后,右击检查,我们发现有些字体并没有正常在源码中显示,如图所示:


这就是重点了,它的反爬虫技术真面目,让你拿不到完整的信息!

接下来爬取,,我们先爬取一部分网页源代码来看看它的真相

1.2、获取网页源代码

import requests
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
url = 'https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-1.html'
requests.get(url=url, headers=headers).text


结果分析: 上面&#xe...不真是网页源码中隐藏住的字体吗?这是一种字体编码,你会发现,同一个字的编码是相同的。如何进行字体反爬我就不具体讲解了,具体内容可以参看这位博主的文章,也很感谢这位博主的文章给予帮助!
参考文章: https://blog.csdn.net/zwq912318834/article/details/80268149

如果你不喜欢给电脑安装软件的话,可以使用“百度字体编辑器”来查看它的字体编码
地址: http://fontstore.baidu.com/static/editor/index.html

1.3、用户随机代理

  • 既然是要模拟客户端,不可能只用一个用户去获取大量的信息,使用随机代理,就可以减少被识别反爬的概率,如果有必要,还可以加上IP代理,就不深入探讨了。
from fake_useragent import UserAgent
#随机生成5个不同的浏览器代理
for i in range(5):headers = {"User-Agent" : UserAgent().chrome #chrome浏览器代理
#         "User-Agent" : UserAgent().random #任意浏览器代理}print (headers)

输出结果:

{'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'}
{'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'}
{'User-Agent': 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36'}
{'User-Agent': 'Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36'}
{'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36'}

1.4、字体替换

# -*- coding:utf-8 -*-
import requests
from lxml import html
import re
from fontTools.ttLib import TTFont# 定义字体文件的名字
fontFileName = "autohomeFont.ttf"
headerInfo = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",'host':'club.autohome.com.cn',
}
# 爬取链接
url = "https://club.autohome.com.cn/bbs/thread/6e20dd257e96ce65/84283887-1.html"#评论+回复
# 获取页面源代码
resp = requests.get(url, headers = headerInfo)# 用正则表达式提取ttf字体文件的地址
# url('//k3.autoimg.cn/g24/M06/3F/FF/wKgHH1qWFoGATQa3AABhmFKVVrQ43..ttf') format('woff');}
ttfUrlRe = re.search(",url\('(//.*.ttf)'\) format\('woff'\)", resp.text, re.DOTALL)
ttfUrl = ""
if ttfUrlRe:ttfUrl = "https:" + ttfUrlRe.group(1)
if ttfUrl:# 以文件流的方式,抓取ttf字体文件ttfFileStream = requests.get(ttfUrl, stream = True)# 将数据流保存在本地的ttf文件中(新创建)with open(fontFileName, "wb") as fp:for chunk in ttfFileStream.iter_content(chunk_size=1024):if chunk:fp.write(chunk)# 用fontTools模块解析字体库文件fontObject = TTFont(fontFileName)# 按顺序拿到各个字符的unicode编码# ['.notdef', 'uniED8F', 'uniED3D', …… ]uniWordList = fontObject['cmap'].tables[0].ttFont.getGlyphOrder()
#     print(f"自定义字体列表(unicorn编码): {uniWordList}")# 将各个字符的unicode编码转换成utf-8编码# [b'\xee\xb6\x8f', b'\xee\xb4\xbd', b'\xee\xb7\xb1', …… ]utf8WordList = [eval("u'\\u" + uniWord[3:] + "'").encode("utf-8") for uniWord in uniWordList[1:]]
#     print(f"自定义字体列表( utf-8 编码): {utf8WordList}")# 获取发帖内容文字response = html.fromstring(resp.text)contentLst = response.xpath("//div[@class='tz-paragraph']//text()")descriptions = response.xpath("//div[@class='description']//text()")comments = response.xpath('//*[@id="maxwrap-reply"]//div[@class="w740"]/text()')replys = response.xpath('//*[@id="maxwrap-reply"]//div[@class="w740"]/div[last()]/text()')# 这个部分的逻辑需要特别注意,因为自定义字体,也就是隐藏字符是以utf-8的形式存在的# 所有一开始,我们就要以utf-8的编码形式来保持文本内容content = ''.encode("utf-8")for elem in contentLst:content += elem.encode("utf-8")# 对图片的描述description = ''.encode("utf-8")for elem in descriptions:description += elem.encode("utf-8")#评论    comment = ''.encode("utf-8")    for elem in comments:comment += elem.encode("utf-8")# 回复   reply = ''.encode("utf-8")    for elem in replys:reply += elem.encode("utf-8")    # 录入字体文件中的字符。必须要以国际标准的unicode编码,取代汽车之家自己定义的字体编码# 这个部分目前是手动输入,但是多次请求,每次拿到的ttf文件可能都不一样,甚至同一个字形,在不同的ttf文件中编码也不同,这个部分需要尤其注意# 因为是python3,所以这些字符直接就是Unicode编码wordList = ['一', '七', '三', '上', '下', '不', '九', '了', '二', '五', '低', '八','六', '十', '的', '着', '近', '远', '长', '右', '呢', '和', '四', '地', '坏','多', '大', '好', '小', '少', '短', '矮', '高', '左', '很', '得', '是', '更',]
#     print(f"字体文件中字形列表: {wordList}")print(f"contentBefort = {content.decode('utf-8')}")print('--------------- After Convert -----------------')# 因为之前提到过,在网页源代码中,这种“” 特殊字符是utf-8编码,所以我们要以utf-8的模式去进行查找替换# content 是字符串,是Unicode编码for i in range(len(utf8WordList)):# 将自定的字体信息,替换成国际标准content = content.replace(utf8WordList[i], wordList[i].encode('utf-8'))description = description.replace(utf8WordList[i], wordList[i].encode('utf-8'))comment = comment.replace(utf8WordList[i], wordList[i].encode('utf-8'))reply = reply.replace(utf8WordList[i], wordList[i].encode('utf-8'))content = str(content.decode('utf-8'))description = str(description.decode('utf-8'))    comment = str(comment.decode('utf-8')).replace("\r\n","").replace(" ","")reply = str(reply.decode('utf-8')).replace("\r\n","")print("content = ",content)print("description = ",description)print("comment = ",comment)print("reply = ",reply)

替换结果:

1.5、爬取论坛链接主题链接

1.5.1、构造论坛首页翻页链接

  • 如何看一个网页的链接是怎么构造的,我们可以通过点击下一页,并多复制几个链接来进行对比,或者改变一些参数来进行尝试。
  • 论坛1~8页的链接:
1 https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=1&entry=44
2 https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=2
3 https://sou.autohome.com.cn/luntan?entry=44&page=3&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4
4 https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=4&error=05 https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=5&entry=44
6 https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=6
7 https://sou.autohome.com.cn/luntan?entry=44&page=7&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4
8 https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=8&error=0
  • 可以很容易的分析出来,它的URL相邻的四个都不一样,每四个就要重复一次格式,那就好办了。
for i in range(1, 52):if i%4 == 1:        index_url = "https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=" + str(i) + "&entry=44"        elif i%4 == 2:index_url = "https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=" +str(i)        elif i%4 == 3:index_url = "https://sou.autohome.com.cn/luntan?entry=44&page=" + str(i) + "&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4"        else:index_url = "https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=" + str(i) + "&error=0"print ("%s: %s"%(i,index_url))

生成结果:

1: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=1&entry=44
2: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=2
3: https://sou.autohome.com.cn/luntan?entry=44&page=3&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4
4: https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=4&error=0
5: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=5&entry=44
6: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=6
7: https://sou.autohome.com.cn/luntan?entry=44&page=7&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4
8: https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=8&error=0
9: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=9&entry=44
10: https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=10
11: https://sou.autohome.com.cn/luntan?entry=44&page=11&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4
12: https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=12&error=0

结论: 其实,所有链接都是一样的格式,它也是可以正常访问服务器的,只是,爬虫模拟客户端时,尽量把它模仿到位,减少被反爬的概率。

1.5.2、爬取论点链接

  • 论坛的主页几乎没有什么反爬措施,拿到链接是相当容易的,我直接贴代码了
from lxml import etree
import requests
from fake_useragent import UserAgent
#2019年起一共有51个页面
index = "https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0"
headers = {"User-Agent" : UserAgent().chrome#使用随机代理
}
html = requests.get(url=index, headers=headers).text
etree = etree.HTML(html)
for k in etree.xpath('//*[@id="content"]/div[1]/div[2]/div/dl'):link = k.xpath('./dt/a/@href')[0]print (link)

爬取结果:

http://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-1.html
http://club.autohome.com.cn/bbs/thread/b4fbeb293d318cc2/84431220-1.html
http://club.autohome.com.cn/bbs/thread/b091dc1bafae9e96/84409274-1.html
http://club.autohome.com.cn/bbs/thread/6e20dd257e96ce65/84283887-1.html
http://club.autohome.com.cn/bbs/thread/69c3430a83c283f9/84155617-1.html
http://club.autohome.com.cn/bbs/thread/298e255cc86981e1/84001022-1.html
http://club.autohome.com.cn/bbs/thread/f244b3672b1cb9be/83908505-1.html
http://club.autohome.com.cn/bbs/thread/221a469e9ce0b1e6/83480757-1.html
http://club.autohome.com.cn/bbs/thread/49f989239d788ab8/83341349-1.html

1.6、实现评论内容翻页

  • 有些文章的评论有很多的页面,每一个页面的链接都需要爬取到,但是它往往又不是完整的,可能会隐藏点中间部分的链接,如下图所示:

  • 对于这样的情况,选择构造链接是一个不错的选择

  • 思路:

  1. 获取源代码
  2. 提取页数最大值
  3. 判断:只有一个链接时,就是原链接;第二个链接通过原链接的 “-” 切分,加上当前页数值,再拼接上“.html”
import requests
import re
from lxml import html
from fake_useragent import UserAgentcontent_url = "https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-1.html"
headers = {"User-Agent" : UserAgent().chrome#使用随机代理
}
resp = requests.get(url=content_url, headers=headers)
response = html.fromstring(resp.text)
maxPages = response.xpath("//span[@class='fs']/text()")[0]#找到评论的页数
maxPage = re.sub(r'\D', "", maxPages)#提取数字
maxPage = int(maxPage)
for page in range(1, maxPage+1):if page == 1:print ("这篇文章共有%s页,正在爬取第1页"%(maxPage))print (content_url)passelse:content_url = content_url.split('-')[0] + "-" + str(page) + ".html"#自己构造链接print ("这篇文章共有%s页,正在爬取第%s页"%(maxPage, page))print (content_url)

运行结果:

这篇文章共有21页,正在爬取第1页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-1.html
这篇文章共有21页,正在爬取第2页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-2.html
这篇文章共有21页,正在爬取第3页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-3.html
这篇文章共有21页,正在爬取第4页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-4.html
这篇文章共有21页,正在爬取第5页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-5.html
这篇文章共有21页,正在爬取第6页
https://club.autohome.com.cn/bbs/thread/964c33457ece138a/84139333-6.html
......

1.7、大功告成,附上源码

from lxml import etree
from lxml import html
import requests,re,time
from fake_useragent import UserAgent
from fontTools.ttLib import TTFont
import randomstartTime =time.time()#获取开始时的时间
# 定义字体文件的名字
fontFileName = "autohomeFont.ttf"
fileName = "比亚迪新能源论坛.txt"
forum_urls = []#论坛的每一个话题的链接def forum_url_spider():#论坛一共有51个页面在2019-2020/2/6for i in range(1, 10):#1:52,分开采集,避免反爬if i%4 == 1:        index_url = "https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=" + str(i) + "&entry=44"        elif i%4 == 2:index_url = "https://sou.autohome.com.cn/luntan?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=44&error=0&page=" +str(i)        elif i%4 == 3:index_url = "https://sou.autohome.com.cn/luntan?entry=44&page=" + str(i) + "&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4"        else:index_url = "https://sou.autohome.com.cn/luntan?entry=44&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=" + str(i) + "&error=0"
#         print ("正在获取论坛首页第%s页链接,一共有51个页面需要爬取"%i)print (index_url)headers = {"User-Agent" : UserAgent().chrome,#使用随机代理}time.sleep(random.randint(1,5))html = requests.get(url=index_url, headers=headers).textetrees = etree.HTML(html)for k in etrees.xpath('//*[@id="content"]/div[1]/div[2]/div/dl'):link = k.xpath('./dt/a/@href')[0]forum_urls.append(link)# 检查该话题有多少页
def ckeck_max_page():for content_url in forum_urls:headers = {"User-Agent" : UserAgent().chrome#使用随机代理}print (content_url)time.sleep(random.randint(1,6))resp_html = requests.get(url=content_url, headers=headers)response = html.fromstring(resp_html.text)maxPages = response.xpath("//span[@class='fs']/text()")[0]maxPage = re.sub(r'\D', "", maxPages)#提取数字maxPage = int(maxPage)for page in range(1, maxPage+1):if page == 1:try:print ("这篇文章共有%s页,正在爬取第1页"%(maxPage))print (content_url)title = response.xpath("//div[@class='maxtitle']/text()")[0]with open (fileName, 'a', encoding='utf-8') as f:f.write(title)f.write("\n")f.close()print ("title = ",title)download_font(resp_html)content_spider(resp_html)except:passelse:content_url = content_url.split('-')[0] + "-" + str(page) + ".html"print ("这篇文章共有%s页,正在爬取第%s页"%(maxPage, page))print (content_url)time.sleep(random.randint(1,6))resp_html = requests.get(url=content_url, headers=headers)download_font(resp_html)content_spider(resp_html)def download_font(resp_html):# 用正则表达式提取ttf字体文件的地址# url('//k3.autoimg.cn/g24/M06/3F/FF/wKgHH1qWFoGATQa3AABhmFKVVrQ43..ttf') format('woff');}ttfUrlRe = re.search(",url\('(//.*.ttf)'\) format\('woff'\)", resp_html.text, re.DOTALL)ttfUrl = ""if ttfUrlRe:ttfUrl = "https:" + ttfUrlRe.group(1)if ttfUrl:# 以文件流的方式,抓取ttf字体文件ttfFileStream = requests.get(ttfUrl, stream = True)# 将数据流保存在本地的ttf文件中(新创建)with open(fontFileName, "wb") as fp:for chunk in ttfFileStream.iter_content(chunk_size=1024):if chunk:fp.write(chunk) def content_spider(resp_html):# 用fontTools模块解析字体库文件fontObject = TTFont(fontFileName)# 按顺序拿到各个字符的unicode编码# ['.notdef', 'uniED8F', 'uniED3D', …… ]uniWordList = fontObject['cmap'].tables[0].ttFont.getGlyphOrder()
#     print(f"自定义字体列表(unicorn编码): {uniWordList}")# 将各个字符的unicode编码转换成utf-8编码# [b'\xee\xb6\x8f', b'\xee\xb4\xbd', b'\xee\xb7\xb1', …… ]utf8WordList = [eval("u'\\u" + uniWord[3:] + "'").encode("utf-8") for uniWord in uniWordList[1:]]
#     print(f"自定义字体列表( utf-8 编码): {utf8WordList}")# 获取发帖内容文字response = html.fromstring(resp_html.text)contentLst = response.xpath("//div[@class='tz-paragraph']//text()")descriptions = response.xpath("//div[@class='description']//text()")comments = response.xpath('//*[@id="maxwrap-reply"]//div[@class="w740"]/text()')replys = response.xpath('//*[@id="maxwrap-reply"]//div[@class="w740"]/div[last()]/text()')# 这个部分的逻辑需要特别注意,因为自定义字体,也就是隐藏字符是以utf-8的形式存在的# 所有一开始,我们就要以utf-8的编码形式来保持文本内容content = ''.encode("utf-8")for elem in contentLst:content += elem.encode("utf-8")# 对图片的描述description = ''.encode("utf-8")for elem in descriptions:description += elem.encode("utf-8")#评论    comment = ''.encode("utf-8")    for elem in comments:comment += elem.encode("utf-8")# 回复   reply = ''.encode("utf-8")    for elem in replys:reply += elem.encode("utf-8")    # 录入字体文件中的字符。必须要以国际标准的unicode编码,取代汽车之家自己定义的字体编码# 这个部分目前是手动输入,但是多次请求,每次拿到的ttf文件可能都不一样,甚至同一个字形,在不同的ttf文件中编码也不同,这个部分需要尤其注意# 因为是python3,所以这些字符直接就是Unicode编码wordList = ['一', '七', '三', '上', '下', '不', '九', '了', '二', '五', '低', '八','六', '十', '的', '着', '近', '远', '长', '右', '呢', '和', '四', '地', '坏','多', '大', '好', '小', '少', '短', '矮', '高', '左', '很', '得', '是', '更',]# 因为之前提到过,在网页源代码中,这种“” 特殊字符是utf-8编码,所以我们要以utf-8的模式去进行查找替换# content 是字符串,是Unicode编码for i in range(len(utf8WordList)):# 将自定的字体信息,替换成国际标准content = content.replace(utf8WordList[i], wordList[i].encode('utf-8'))description = description.replace(utf8WordList[i], wordList[i].encode('utf-8'))comment = comment.replace(utf8WordList[i], wordList[i].encode('utf-8'))reply = reply.replace(utf8WordList[i], wordList[i].encode('utf-8'))content = str(content.decode('utf-8'))description = str(description.decode('utf-8'))    comment = str(comment.decode('utf-8')).replace("\r\n","").replace(" ","")reply = str(reply.decode('utf-8')).replace("\r\n","")with open (fileName, 'a', encoding='utf-8') as f:f.write(content)f.write(description)f.write(comment)f.write(reply)f.write("\n")f.close()if __name__ == '__main__':forum_url_spider()ckeck_max_page()endTime =time.time()#获取结束时的时间useTime =(endTime-startTime)/60print ("该次所获的信息一共使用%s分钟"%useTime)

2、汽车之家问答

  • 问答部分和论坛部分的方式是一样的,就不再重述了,如果不明白的地方,可以看看论坛部分。
from lxml import etree
from lxml import html
import requests,re,time
from fake_useragent import UserAgent
from fontTools.ttLib import TTFont
import randomstartTime =time.time()#获取开始时的时间
# 定义字体文件的名字
fontFileName = "autohomeFont.ttf"
fileName = "比亚迪新能源问答.txt"
forum_urls = []#论坛的每一个话题的链接def forum_url_spider():#问答一共有8个页面for i in range(1, 9):if i%4 == 1:        index_url = "https://sou.autohome.com.cn/zhidao?entry=90&page=" + str(i) + "&error=0&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4"elif i%4 == 2:index_url = "https://sou.autohome.com.cn/zhidao?entry=90&q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&page=" + str(i) + "&error=0"elif i%4 == 3:index_url = "https://sou.autohome.com.cn/zhidao?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&error=0&page=" + str(i) + "entry=90"else:index_url = "https://sou.autohome.com.cn/zhidao?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&entry=90&error=0&page=" + str(i)print (index_url)headers = {"User-Agent" : UserAgent().chrome #使用随机代理}time.sleep(random.randint(1,5))html = requests.get(url=index_url, headers=headers).textetrees = etree.HTML(html)for k in etrees.xpath('//*[@id="content"]/div[1]/div[2]/div/dl'):link = k.xpath('./dt/a/@href')[0]print (link)forum_urls.append(link)# 检查该话题有多少页
def ckeck_max_page():for content_url in forum_urls:headers = {"User-Agent" : UserAgent().chrome #使用随机代理}print (content_url)time.sleep(random.randint(1,4))resp_html = requests.get(url=content_url, headers=headers)response = html.fromstring(resp_html.text)maxPages = response.xpath("//span[@class='fs']/text()")[0]maxPage = re.sub(r'\D', "", maxPages)#提取数字maxPage = int(maxPage)for page in range(1, maxPage+1):if page == 1:print ("这篇文章共有%s页,正在爬取第1页"%(maxPage))print (content_url)title = response.xpath("//div[@class='qa-maxtitle']/text()")[0]with open (fileName, 'a', encoding='utf-8') as f:f.write(title)f.write("\n")f.close()print ("title = ",title)download_font(resp_html)content_spider(resp_html)                else:content_url = content_url.split('-')[0] + "-" + str(page) + ".html"print ("这篇文章共有%s页,正在爬取第%s页"%(maxPage, page))print (content_url)time.sleep(random.randint(1,4))resp_html = requests.get(url=content_url, headers=headers)download_font(resp_html)content_spider(resp_html)def download_font(resp_html):# 用正则表达式提取ttf字体文件的地址# url('//k3.autoimg.cn/g24/M06/3F/FF/wKgHH1qWFoGATQa3AABhmFKVVrQ43..ttf') format('woff');}ttfUrlRe = re.search(",url\('(//.*.ttf)'\) format\('woff'\)", resp_html.text, re.DOTALL)ttfUrl = ""if ttfUrlRe:ttfUrl = "https:" + ttfUrlRe.group(1)if ttfUrl:# 以文件流的方式,抓取ttf字体文件ttfFileStream = requests.get(ttfUrl, stream = True)# 将数据流保存在本地的ttf文件中(新创建)with open(fontFileName, "wb") as fp:for chunk in ttfFileStream.iter_content(chunk_size=1024):if chunk:fp.write(chunk) def content_spider(resp_html):# 用fontTools模块解析字体库文件fontObject = TTFont(fontFileName)# 按顺序拿到各个字符的unicode编码# ['.notdef', 'uniED8F', 'uniED3D', …… ]uniWordList = fontObject['cmap'].tables[0].ttFont.getGlyphOrder()
#     print(f"自定义字体列表(unicorn编码): {uniWordList}")# 将各个字符的unicode编码转换成utf-8编码# [b'\xee\xb6\x8f', b'\xee\xb4\xbd', b'\xee\xb7\xb1', …… ]utf8WordList = [eval("u'\\u" + uniWord[3:] + "'").encode("utf-8") for uniWord in uniWordList[1:]]
#     print(f"自定义字体列表( utf-8 编码): {utf8WordList}")# 获取发帖内容文字response = html.fromstring(resp_html.text)contentLst = response.xpath("//div[@class='w740']//text()")# 这个部分的逻辑需要特别注意,因为自定义字体,也就是隐藏字符是以utf-8的形式存在的# 所有一开始,我们就要以utf-8的编码形式来保持文本内容content = ''.encode("utf-8")for elem in contentLst:content += elem.encode("utf-8")# 录入字体文件中的字符。必须要以国际标准的unicode编码,取代汽车之家自己定义的字体编码# 这个部分目前是手动输入,但是多次请求,每次拿到的ttf文件可能都不一样,甚至同一个字形,在不同的ttf文件中编码也不同,这个部分需要尤其注意# 因为是python3,所以这些字符直接就是Unicode编码wordList = ['一', '七', '三', '上', '下', '不', '九', '了', '二', '五', '低', '八','六', '十', '的', '着', '近', '远', '长', '右', '呢', '和', '四', '地', '坏','多', '大', '好', '小', '少', '短', '矮', '高', '左', '很', '得', '是', '更',]# 因为之前提到过,在网页源代码中,这种“” 特殊字符是utf-8编码,所以我们要以utf-8的模式去进行查找替换# content 是字符串,是Unicode编码for i in range(len(utf8WordList)):# 将自定的字体信息,替换成国际标准content = content.replace(utf8WordList[i], wordList[i].encode('utf-8'))content = str(content.decode('utf-8')).replace("\r\n","").replace(" ","").replace(" ","\n")with open (fileName, 'a', encoding='utf-8') as f:f.write(content)f.write("\n")f.close()if __name__ == '__main__':forum_url_spider()ckeck_max_page()endTime =time.time()#获取结束时的时间useTime =(endTime-startTime)/60print ("该次所获的信息一共使用%s分钟"%useTime)

运行结果截图:

3、汽车之家新闻

3.1、新闻内容

from lxml import html
import requests,re,time
from fake_useragent import UserAgentarticle_url = "https://www.autohome.com.cn/news/201909/945418.html"
headers = {"User-Agent" : UserAgent().chrome
}
article_html = requests.get(url=article_url, headers=headers)
response = html.fromstring(article_html.text)
href = response.xpath('//*[@id="hudongreply"]//@href')
comment_url = "https:" + href[0] #获取到更多评论链接
print ("comment_url = ", comment_url)
#文章标题
article_details = response.xpath('//*[@id="articlewrap"]/h1/text()')[0].replace("\n","").replace(" ", "")
print ("article_title = ", article_details)
#文章内容
contentLst = response.xpath('//*[@id="articleContent"]//p//text()')
content = "".join(contentLst).replace("\u3000\u3000","").replace("\xa0","")
print ("content = ", content)

3.1、新闻评论

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from lxml import html
import timecomment_url = "https://www.autohome.com.cn/comment/Articlecomment.aspx?articleid=945418#pvareaid=3311690"
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')#上面三行代码就是为了将Chrome不弹出界面,实现无界面爬取
driver = webdriver.Chrome(chrome_options=chrome_options)option = webdriver.ChromeOptions()
option.add_argument('--proxy--server=112.84.55.122:9999')#使用代理IPdriver.get(comment_url)#打开网页网页
driver.implicitly_wait(6)#等待加载六秒
maxPage = driver.find_element_by_xpath('//*[@id="topPager"]/div/a[last()-1]').text
maxPage = int(maxPage)
for i in range(maxPage):time.sleep(3)source = driver.page_sourceresponse = html.fromstring(source)user_comment = response.xpath('//*[@id="reply-list"]/dd/p/text()')user_comment = "".join(user_comment)print (user_comment)driver.find_element_by_xpath('//*[@id="topPager"]/div/a[last()]').click()
driver.quit()#推出并关闭浏览器

4、汽车之家文章

  • 20190101~20200206共有文章约72页,每页10篇,约720篇。

注意: 汽车之家的文章包括之家原创的新闻中心 和 用户文章的车家号两个页组成。

  • 上面已经讲解了对于新闻及其评论的爬取,现在只需要加上车家号就行了,由于车家号的车主文章评论太少,就不对它的评论进行爬取了。

4.1、爬取所有文章的链接

from lxml import etree
import requests
from fake_useragent import UserAgentarticle_urls = [] #文章链接
for i in range(1, 5):index_url = "https://sou.autohome.com.cn/wenzhang?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&searchtypeContent=content&sort=New&entry=43&class=0&error=0&page=" + str(i)headers = {"User-Agent" : UserAgent().chrome}index_html = requests.get(url=index_url, headers=headers).textetrees = etree.HTML(index_html)for k in etrees.xpath('//*[@id="content"]/div[1]/div[2]/div/dl'):link = k.xpath('./dt/a/@href')[0]print (link)article_urls.append(link)

输出结果:

https://chejiahao.autohome.com.cn/info/5566800#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5564867#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5563583#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5563099#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5562095#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5560884#pvareaid=28086821202
http://www.autohome.com.cn/news/202002/968713.html
https://chejiahao.autohome.com.cn/info/5557390#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5557241#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5555108#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5516371#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5528244#pvareaid=28086821202
https://chejiahao.autohome.com.cn/info/5521957#pvareaid=28086821202

4.2、判断新闻和车家号

  • 由于新闻和车家号的网页结构不一样,所以必须要分开爬取。它俩的链接参数也不一样,所以可以通过关键词来进行区别它们。
for article_url in article_urls:if "news" in article_url:print ("这是新闻页:", article_url)if "info" in article_url:print ("这是车家号:", article_url)

判断结果:

这是车家号: https://chejiahao.autohome.com.cn/info/5566800#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5564867#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5563583#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5563099#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5562095#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5560884#pvareaid=28086821202
这是新闻页: http://www.autohome.com.cn/news/202002/968713.html
这是车家号: https://chejiahao.autohome.com.cn/info/5557390#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5557241#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5555108#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5516371#pvareaid=28086821202
这是车家号: https://chejiahao.autohome.com.cn/info/5528244#pvareaid=28086821202

4.3、车家号文章

from lxml import html
import requests,re,time
from fake_useragent import UserAgentarticle_url = "https://chejiahao.autohome.com.cn/info/5566800#pvareaid=28086821202"
headers = {"User-Agent" : UserAgent().chrome
}
article_html = requests.get(url=article_url, headers=headers)
response = html.fromstring(article_html.text)contentLst = response.xpath('//div[@class="introduce_content"]//text()')
content = "".join(contentLst).replace("\n","").replace(" ", "")
if len(contentLst) == 0:contentLst = response.xpath('//p[@class="text"]//text()')content = "".join(contentLst).replace("\n","").replace(" ", "")
print ("content = ", content)

4.4、代码汇总

from lxml import etree
from lxml import html
import requests,re,time
from fake_useragent import UserAgent
import random
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import OptionsstartTime =time.time()#获取开始时的时间
fileName = "比亚迪新能源文章.txt"article_urls = [] #文章链接
for i in range(1, 10):index_url = "https://sou.autohome.com.cn/wenzhang?q=%b1%c8%d1%c7%b5%cf%d0%c2%c4%dc%d4%b4&searchtypeContent=content&sort=New&entry=43&class=0&error=0&page=" + str(i)headers = {"User-Agent" : UserAgent().chrome}index_html = requests.get(url=index_url, headers=headers).textetrees = etree.HTML(index_html)for k in etrees.xpath('//*[@id="content"]/div[1]/div[2]/div/dl'):link = k.xpath('./dt/a/@href')[0]article_urls.append(link)
for count,article_url in enumerate(article_urls):try:print ("正在爬取第%s个,一共有%s个"%(count+1, len(article_urls)))headers = {"User-Agent" : UserAgent().chrome}article_html = requests.get(url=article_url, headers=headers)response = html.fromstring(article_html.text)#之家原创评论if "news" in article_url:href = response.xpath('//*[@id="hudongreply"]//@href')comment_url = "https:" + href[0]#文章标题article_details = response.xpath('//*[@id="articlewrap"]/h1/text()')[0].replace("\n","").replace(" ", "")with open(fileName, "a", encoding="utf-8") as f:f.write(article_details)f.close()print ("article_details = ", article_details)contentLst = response.xpath('//*[@id="articleContent"]//p//text()')content = "".join(contentLst).replace("\u3000\u3000","").replace("\xa0","")with open(fileName, "a", encoding="utf-8") as f:f.write(content)f.close()chrome_options = Options()chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')#上面三行代码就是为了将Chrome不弹出界面,实现无界面爬取driver = webdriver.Chrome(chrome_options=chrome_options)option = webdriver.ChromeOptions()option.add_argument('--proxy--server=112.84.55.122:9999')#使用代理IPdriver.get(comment_url)#打开网页网页driver.implicitly_wait(6)#等待加载六秒maxPage = driver.find_element_by_xpath('//*[@id="topPager"]/div/a[last()-1]').textmaxPage = int(maxPage)for i in range(maxPage):time.sleep(2)source = driver.page_sourceresponse = html.fromstring(source)user_comment = response.xpath('//*[@id="reply-list"]/dd/p/text()')user_comment = "".join(user_comment)with open(fileName, "a", encoding="utf-8") as f:f.write(content)f.close()driver.find_element_by_xpath('//*[@id="topPager"]/div/a[last()]').click()driver.quit()#推出并关闭浏览器# 车友文章if "info" in article_url:contentLst = response.xpath('//div[@class="introduce_content"]//text()')content = "".join(contentLst).replace("\n","").replace(" ", "")if len(contentLst) == 0:contentLst = response.xpath('//p[@class="text"]//text()')content = "".join(contentLst).replace("\n","").replace(" ", "")with open(fileName, "a", encoding="utf-8") as f:f.write(content)f.close()except:pass
endTime =time.time()#获取结束时的时间
useTime =(endTime-startTime)/60
print ("该次所获的信息一共使用%s分钟"%useTime)

程序分开进行:

运行结果:

  • 上面这段程序是我在晚上4点钟运行的,同时分为7个程序爬取,虽然是手机wifi,约7分钟爬完720篇文章。

5、结果汇总截图

倡议: 我们快速爬取到自己想要的是一个方面,但是不建议大家在目标网站访问量大的时候去批量爬取别人网站,很容易给别人的服务器造成压力。

《汽车之家》字体反爬之论坛、问答、文章(新闻、车家号)及其评论爬取相关推荐

  1. pythonttf字体反爬虫_Python爬虫---汽车之家字体反爬

    本篇文章给大家带来的内容是关于Python爬虫---汽车之家字体反爬,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 目标网站:汽车之家 目标网址:https://club.autoh ...

  2. 爬虫:汽车之家字体反爬

    1.我的目的 汽车之家对某些字体进行了反爬虫操作,直接获取是一堆乱码,这次要做的是对这些乱码进行转化,转化为我们想要的符号\汉字 2.前提知识 首先得了解一下字体反爬虫是什么意思: 当前css可以指定 ...

  3. python爬虫进阶-汽车之家贴吧信息(字体反爬-动态映射)

    目的 获取汽车之家贴吧的内容信息 详细需求 汽车之家贴吧 思路解析 一.F12获取目标信息-进行分析 二.字体反爬解析-根据上一篇的文章,直接搜索关键词就好 三 根据其后的链接,保存为ttf在本地,查 ...

  4. python爬虫笔记五:汽车之家贴吧信息(字体反爬-动态映射)

    学习网址: https://jia666666.blog.csdn.net/article/details/108974149 ----------------------------------- ...

  5. Python爬虫六:字体反爬处理(猫眼+汽车之家)-2018.10

    环境:Windows7 +Python3.6+Pycharm2017 目标:猫眼电影票房.汽车之家字体反爬的处理 ---全部文章: 京东爬虫 .链家爬虫.美团爬虫.微信公众号爬虫.字体反爬.Djang ...

  6. 如何去除网页噪声提取数据(02) —— 汽车之家(字体反爬)

    如何去除网页噪声提取数据(02) -- 汽车之家(字体反爬) 1. 需求介绍 继去哪儿网之后,我又盯上了汽车之家这个网站,这个网站的反爬策略挺有意思的,采用了时下最流行的字体反爬技术,让我心神荡漾,对 ...

  7. Python爬虫:字体反爬处理(猫眼+汽车之家)-2018.10

    环境:Windows7 +Python3.6+Pycharm2017 目标:猫眼电影票房.汽车之家字体反爬的处理 -全部文章: 京东爬虫 .链家爬虫.美团爬虫.微信公众号爬虫.字体反爬.Django笔 ...

  8. 笔记 - 汽车之家的反爬手段

    一.字体反爬 方法转自:汽车之家字体反爬破解实践 - 谢俊杰的文章 - 知乎 并稍加修改(更换要爬的页面,python2改为python3) 假如我们要爬这个页面https://k.autohome. ...

  9. 字体反爬——可视化字符匹配通用方案(浏览器版)

    [md]在爬取数据的时候,一些网站做了关于字体反爬的处理,他们反爬原理大致都差不多,用制作的字符图标替换为真实文字显示,导致你去复制他的东西,显示出来就变成一堆乱码 反爬例子 来看几个例子: 其中处理 ...

  10. 爬虫学习笔记(二十)—— 字体反爬

    文章目录 一.什么是字体反爬 二.编码原理 2.1.ASCII编码对照表 2.2.Unicode编号 2.3.UTF-8编码方式 2.4.字符矢量图 三.案例:58同城反爬字体 3.1.代码实现 一. ...

最新文章

  1. 【转载】阿里云ECS服务器监控资源使用情况
  2. Mac OS X snow leopard 10.6 VMware安装
  3. 为了不让代码“作恶”,能否将道德条款纳入开源许可证?
  4. LeetCode简单题目(二叉树相关)(#100 #101 #104 #107 #108 #110 #111 #112)-8道
  5. python教程视频-私藏已久的7个Python视频教程
  6. 项目需求:基于微信平台的拼团活动系统
  7. java outofmerroy_Tomcat中的Out Of Memory错误
  8. libgdx 3D CameraInputController WASD控制器
  9. wince只运行一次应用程序
  10. 如何做好离职交接工作?
  11. 地图测量面积工具app_GPS地图测量尺
  12. Excel一元线性回归分析
  13. 研发思维09----嵌入式智能产品开发经过思考
  14. 关于免费域名和空间搭建个人网站——服务器篇
  15. C++ std::vector 的 emplace_back 能否完全取代 push_back
  16. Replace Autoprefixer browsers option to Browserslist config,VUECLI3报错
  17. mpstat命令(linux cpu监控工具)
  18. Vue+Vuex+Axios+ECharts 画一个动态更新的中国地图
  19. 评论:UiPath Aces机器人流程自动化
  20. 杭电OJ 1026(C++)

热门文章

  1. 【PMP】PMBOK 笔记 第10章 项目沟通管理
  2. <<道 德 经>>------老子
  3. POI 模板生成word PDF——牛X神器
  4. B站有哪些程序员大牛up主?
  5. 解决 MDK 5 无法生成 .axf 文件的问题
  6. 面试时你需要问HR什么问题?
  7. HP笔记本电脑如何设置屏幕亮度
  8. PHP 简单开发实例
  9. 虹科OPC UA SDK案例:虹科OPC UA SDK助力立功科技ZWS云平台
  10. 2021-2025年中国IT业的利好机遇