美文网首页
如何利用 C# + Python 破解猫眼电影的反爬虫机制?

如何利用 C# + Python 破解猫眼电影的反爬虫机制?

作者: 老马的程序人生 | 来源:发表于2019-04-11 19:45 被阅读0次

如何利用 C# 爬取「猫眼电影:最受期待榜」及对应影片信息! 这篇图文中可以看到猫眼电影对“本月新增想看人数” 和 “总想看人数”进行了字符集加密。

字体编码

如何利用 C# 爬取「猫眼电影:国内票房榜」及对应影片信息! 这篇图文中也可以看到猫眼电影对“实时票房” 和 “总票房”进行了字符集加密。

字体编码

破解这种利用字符集加密的反爬虫机制,需要建立映射关系表,找到每个字符对应的真实数字即可。怎么做呢?

我们 首先 需要把对应的字符集下载到本地。

stonefont类

然后,把该字符集转换成XML文件。由于 Python 中有 TTFont 指令可以直接使用,于是把转换的功能用 Python 来写并用 pyinstaller 指令打包成 EXE 文件,通过 C# 语言进行调用即可。

imports sys
from fontTools.ttLib import TTFont

if __name__ =='__main__'
    font1 = TTFont(str(sys.argv[1]))
    font1.saveXML(str(sys.argv[2]))

打包main.py文件的代码:

pyinstaller -F main.py

生成的 XML 文件如下:

图形信息

接着,建立映射关系表,把TTGlyph namecountour和真实的数据对应起来。这个不能自动化只能把网页上的数字与源代码中的字符对应,在通过源代码中的字符与这个XML中的 TTGlyph name对应。

映射关系

最后,可以通过加载这份映射表来破解猫眼电影的反爬虫机制。即我先在内存中加载这份映射表,每个字体的图形名称TTGlyph name在刷新后是变化的,但图形信息countour是不会变化的,所以可以先通过图形名称找到图形信息,在通过这份映射表找到对应的真实数字。


以上详细的介绍了破解猫眼电影反爬虫机制的方法,下面我们来写具体的代码。

1. 构造字体图形的结构 FontGlyph

public class FontGlyph
{
    /// <summary>
    /// 图形名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 图形数据
    /// </summary>
    public string Glyph { get; set; }

    /// <summary>
    /// 图形值
    /// </summary>
    public int Value { get; set; } = -1;
}

2. 获取字体 WOFF 文件的存储地址

private static string GetWoffUrl(string str)
{
    int end = str.IndexOf(@"') format('woff')", StringComparison.Ordinal);
    int start = str.LastIndexOf("//vfile.meituan.net", StringComparison.Ordinal);
    string url = str.Substring(start, end - start);
    return "http:" + url;
}

3. 下载字体 WOFF 文件到本地

private static string Download(string url)
{
    int index = url.LastIndexOf("/", StringComparison.Ordinal);
    string fileName = @".\font\" + url.Substring(index + 1);

    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;

    if (request == null)
        return string.Empty;

    HttpWebResponse response = request.GetResponse() as HttpWebResponse;

    if (response == null)
        return string.Empty;

    Stream reponseStream = response.GetResponseStream();
    if (reponseStream == null)
        return string.Empty;

    Stream stream = new FileStream(fileName, FileMode.Create);
    byte[] bArr = new byte[1024];
    int size = reponseStream.Read(bArr, 0, bArr.Length);
    while (size > 0)
    {
        stream.Write(bArr, 0, size);
        size = reponseStream.Read(bArr, 0, bArr.Length);
    }
    stream.Close();
    reponseStream.Close();
    return fileName;
}

4. 转换字体 WOFF 文件为XML文件

private static string TransXml(string fileName)
{
    string xml = @".\font\" + Path.GetFileNameWithoutExtension(fileName) + ".xml";

    Process p = new Process();
    p.StartInfo.FileName = @".\font\main.exe";
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.Arguments = fileName + " " + xml;
    p.Start();
    p.WaitForExit();
    p.Close();
    return xml;
}

5. 根据 XML 文件获得字体图形文件列表 List<FontGlyph>

private static List<FontGlyph> GetXmlGlyf(string path)
{
    List<FontGlyph> result = new List<FontGlyph>();
    XmlDocument xmldoc = new XmlDocument();
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.IgnoreComments = true;
    XmlReader reader = XmlReader.Create(path, settings);
    xmldoc.Load(reader);
    if (xmldoc.DocumentElement == null)
        return result;

    XmlNodeList nodeList = xmldoc.DocumentElement.ChildNodes;
    foreach (XmlNode node in nodeList)
    {
        if (node.Name == "glyf")
        {
            XmlNodeList lst = node.ChildNodes;
            foreach (XmlNode n in lst)
            {
                XmlElement xe = n as XmlElement;
                if (xe != null)
                {
                    if (string.IsNullOrEmpty(xe.InnerXml) == false)
                    {
                        FontGlyph item = new FontGlyph();
                        item.Name = xe.GetAttribute("name");
                        item.Glyph = xe.InnerXml;
                        item.Value = -1;
                        result.Add(item);
                    }
                }
            }
        }

    }
    return result;
}

6. 建立映射关系

private static int GetValue(string name)
{
    string[] str = new string[]
    {
        "uniF756", "uniED75", "uniF8F8", 
        "uniF81A", "uniEE70", "uniECA0",
        "uniEC6F", "uniE93B", "uniE9D3",
        "uniF8B5"
    };
    for (int i = 0; i < str.Length; i++)
    {
        if (str[i] == name)
            return i;
    }
    return -1;
}

private static List<FontGlyph> GetFontGlyphList(string fileName)
{
    List<FontGlyph> lst = GetXmlGlyf(fileName);

    for (int i = 0, len = lst.Count; i < len; i++)
    {
        lst[i].Value = GetValue(lst[i].Name);
    }
    return lst;
}

7. 解密算法

private static string Decrypt(string str, List<FontGlyph> lstModel, List<FontGlyph> lstCur)
{
    string result = string.Empty;
    string[] ss = str.Split(new char[] {'.'});
    string[] ss1 = ss[0].Split(new char[] {';'}); //处理整数部分

    for (int i = 0; i < ss1.Length; i++)
    {
        if (string.IsNullOrEmpty(ss1[i]) == false)
        {
            int d = GetIndex(ss1[i], lstCur);
            int v = GetValue(lstCur[d].Glyph, lstModel);
            result += v.ToString();
        }
    }

    if (ss.Length != 1) //处理小数部分
    {
        result += ".";
        string[] ss2 = ss[1].Split(new char[] {';'});
        for (int i = 0; i < ss2.Length; i++)
        {
            if (string.IsNullOrEmpty(ss2[i]) == false)
            {
                int d = GetIndex(ss2[i], lstCur);
                int v = GetValue(lstCur[d].Glyph, lstModel);
                result += v.ToString();
            }
        }
    }
    return result;
}

private static int GetIndex(string str, List<FontGlyph> lstCur)
{
    for (int i = 0, len = lstCur.Count; i < len; i++)
    {
        if (lstCur[i].Name.Contains(str.Substring(3).ToUpper()))
            return i;
    }
    return -1;
}

private static int GetValue(string str, List<FontGlyph> lstModel)
{
    for (int i = 0, len = lstModel.Count; i < len; i++)
    {
        if (lstModel[i].Glyph == str)
        {
            return lstModel[i].Value;
        }
    }
    return -1;
}

8. 获取「猫眼电影:国内票房榜 及对应影片信息」修改后的代码

public static List<Film> GetFilmsTicket()
{

    List<Film> result = new List<Film>();

    string url = "https://maoyan.com/board/1"; //国内票房榜
    IHtmlDocument doc = GetHtmlDocument(url);
    if (doc == null)
        return result;

    List<FontGlyph> lstModel = GetFontGlyphList(@".\font\base.xml");

    List<IHtmlElement> woffStr = doc.Find("style").ToList();
    string tempUrl = GetWoffUrl(woffStr[0].InnerHtml());
    string fileNameWoff = Download(tempUrl);
    string fileNameXml = TransXml(fileNameWoff);
    List<FontGlyph> lstNew = GetXmlGlyf(fileNameXml);

    List<IHtmlElement> lists = doc.Find("dd").ToList();

    for (int i = 0; i < lists.Count; i++)
    {

        List<IHtmlElement> infor = lists[i].Find("p").ToList();

        Film item = new Film();
        item.Num = i + 1; //排名

        string dw, ticket;
        List<IHtmlElement> s;

        if (infor.Count < 5)
        {
            item.Time = infor[1].InnerHtml().Trim().Remove(0, 5); //上映时间
            dw = infor[2].InnerHtml().Trim();
            dw = dw.Remove(0, dw.Length - 1); //实时票房单位

            s = infor[2].Find(".stonefont").ToList();

            ticket = s[0].InnerHtml().Trim(); //加密的实时票房
            item.BoxInfo = Decrypt(ticket, lstModel, lstNew) + dw; //实时票房

            dw = infor[3].InnerHtml().Trim();
            dw = dw.Remove(0, dw.Length - 1); //总票房单位

            s = infor[3].Find(".stonefont").ToList();
            ticket = s[0].InnerHtml().Trim(); //加密的总票房
            item.SumBoxInfo = Decrypt(ticket, lstModel, lstNew) + dw; //总票房
        }
        else
        {
            item.Actor = infor[1].InnerHtml().Trim().Remove(0, 3); //演员
            item.Time = infor[2].InnerHtml().Trim().Remove(0, 5); //上映时间
            dw = infor[3].InnerHtml().Trim();
            dw = dw.Remove(0, dw.Length - 1); //实时票房单位

            s = infor[3].Find(".stonefont").ToList();

            ticket = s[0].InnerHtml().Trim(); //加密的实时票房
            item.BoxInfo = Decrypt(ticket, lstModel, lstNew) + dw; //实时票房

            dw = infor[4].InnerHtml().Trim();
            dw = dw.Remove(0, dw.Length - 1); //总票房单位

            s = infor[4].Find(".stonefont").ToList();
            ticket = s[0].InnerHtml().Trim(); //加密的总票房
            item.SumBoxInfo = Decrypt(ticket, lstModel, lstNew) + dw; //总票房
        }

        IHtmlElement a = infor[0].Find("a").ToList()[0]; //获取影片url
        item.MovieName = a.InnerHtml().Trim(); //名称

        url = "https://maoyan.com" + a.Attribute("href").AttributeValue;
        IHtmlDocument temp = GetHtmlDocument(url);

        List<IHtmlElement> t = temp.Find("li.ellipsis").ToList();
        item.Type = t[0].InnerHtml(); //类型

        List<IHtmlElement> b = temp.Find(".dra").ToList();
        item.Introduction = b[0].InnerHtml(); //介绍

        result.Add(item);
    }
    return result;
}

9. 获取「猫眼电影:最受期待榜及对应影片信息」修改后的代码

public static List<Film> GetFilmsExpect(int offset)
{
    List<Film> result = new List<Film>();

    string url = "https://maoyan.com/board/6"; //最受期待榜
    IHtmlDocument doc = GetHtmlDocument(url, offset);

    if (doc == null)
        return result;

    List<FontGlyph> lstModel = GetFontGlyphList(@".\font\base.xml");

    List<IHtmlElement> woffStr = doc.Find("style").ToList();
    string tempUrl = GetWoffUrl(woffStr[0].InnerHtml());
    string fileNameWoff = Download(tempUrl);
    string fileNameXml = TransXml(fileNameWoff);
    List<FontGlyph> lstNew = GetXmlGlyf(fileNameXml);

    List<IHtmlElement> lists = doc.Find("dd").ToList();

    for (int i = 0; i < lists.Count; i++)
    {
        List<IHtmlElement> infor = lists[i].Find("p").ToList();

        Film item = new Film();
        item.Num = i + 1; //排名

        item.Actor = infor[1].InnerHtml().Trim().Remove(0, 3); //演员
        item.Time = infor[2].InnerHtml().Trim().Remove(0, 5); //上映时间
        string dw = infor[3].InnerHtml().Trim();
        dw = dw.Remove(0, dw.Length - 1); //单位

        List<IHtmlElement> p = infor[3].Find(".stonefont").ToList();

        string people = p[0].InnerHtml().Trim(); //加密的新增想看人数
        item.NewWatcher = Decrypt(people, lstModel, lstNew) + dw; //解密的新增想看人数

        dw = infor[4].InnerHtml().Trim();
        dw = dw.Remove(0, dw.Length - 1); //单位

        p = infor[4].Find(".stonefont").ToList();
        people = p[0].InnerHtml().Trim(); //加密的总想看人数
        item.TotalWatcher = Decrypt(people, lstModel, lstNew) + dw; //解密的总想看人数

        IHtmlElement a = infor[0].Find("a").ToList()[0]; //获取影片url
        item.MovieName = a.InnerHtml().Trim(); //名称

        url = "https://maoyan.com" + a.Attribute("href").AttributeValue;
        IHtmlDocument temp = GetHtmlDocument(url);

        List<IHtmlElement> t = temp.Find("li.ellipsis").ToList();
        item.Type = t[0].InnerHtml(); //类型

        List<IHtmlElement> b = temp.Find(".dra").ToList();
        item.Introduction = b[0].InnerHtml(); //介绍

        result.Add(item);
    }
    return result;
}

10. 输出结果

猫眼电影:国内票房榜 及对应影片信息

结果1

猫眼电影:最受期待榜及对应影片信息

结果2

到此为止,有关如何破解猫眼电影的反爬虫机制就全部介绍完了,破解这种反爬虫机制相对来说比较复杂。如果网站通过用户请求的 Headers 进行反爬虫或者通过限制同一IP短时间内多次访问同一页面进行反爬虫,破解起来就相对容易很多,只要请求时加入相应的 Headers 信息或者利用代理IP绕过就好。今天就到这里吧,See You!


相关图文:

相关文章

网友评论

      本文标题:如何利用 C# + Python 破解猫眼电影的反爬虫机制?

      本文链接:https://www.haomeiwen.com/subject/ciqniqtx.html