NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# 딥러닝을 위한 이미지 크롤링 프로그램 만들기. (전체 소스 코드 포함)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 오늘은 이미지 크롤링 프로그램을 만들어볼까 해요^^; 이미 크롤링 프로그램을 만들어서 무료 배포중이긴한데요. 몇가지 튜닝해야 할것들도 있고 추가로 개선해야 할것들도 있습니다. 그래서, 추가 개선도 할겸 혹시나 필요하신 분들을 위해 정리를 해두려고 합니다. 개발자분들을 위한 내용이므로, 비개발자분들이 이 글을 보고 만들기는 어려울겁니다. 중급정도 개발 지식을 가진 분들은 쉽게 만들 수 있을거예요. 우선 Visual Studio를 설치해야합니다. 아래 링크로 들어간 후 커뮤니티 > 무료 다운로드를 클릭하세요.

    [ 비주얼 스튜디오 다운로드 ]

    kfO4fbh.png

     

     

    비주얼 스튜디오를 무료로 사용하려면 마이크로소프트 회원가입 후 비주얼 스튜디오에서 로그인해야 합니다. 비주얼 스튜디오가 설치되었으면, 새로운 프로젝트에 폼을 하나 추가하세요. 디자인은 아래 그림과 같이 했는데요. 여러분들은 자기만의 디자인으로 해야겠죠^^;

    w7P5mYA.png

     

     

    이것 저것 만사 귀찮다면 아래 디자인 코드를 붙여넣기 하세요. 하단의 홍보용 이미지가 들어있는 PictureBox만 수정하면 됩니다. 코드를 보면 아시겠지만... 귀찮음에 대충 뚝딱 만들다보니 컨트롤들의 이름을 기본으로 사용한게 많습니다. 유지보수가 필요하거나 계속해서 확장해야 한다면 컨트롤 이름과 주고를 좀 더 직관적으로 알 수 있게 수정하는게 좋을겁니다.

    namespace ImageCrawler
    {
        partial class Form1
        {
            /// <summary>
            /// 필수 디자이너 변수입니다.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// 사용 중인 모든 리소스를 정리합니다.
            /// </summary>
            /// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Windows Form 디자이너에서 생성한 코드
    
            /// <summary>
            /// 디자이너 지원에 필요한 메서드입니다. 
            /// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
            /// </summary>
            private void InitializeComponent()
            {
                System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
                this.pictureBox1 = new System.Windows.Forms.PictureBox();
                this.txtOutput = new System.Windows.Forms.RichTextBox();
                this.cboSearchSite = new System.Windows.Forms.ComboBox();
                this.btnSave = new System.Windows.Forms.Button();
                this.txtKeyword = new System.Windows.Forms.TextBox();
                ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
                this.SuspendLayout();
                // 
                // pictureBox1
                // 
                this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
                this.pictureBox1.Location = new System.Drawing.Point(13, 141);
                this.pictureBox1.Name = "pictureBox1";
                this.pictureBox1.Size = new System.Drawing.Size(400, 150);
                this.pictureBox1.TabIndex = 6;
                this.pictureBox1.TabStop = false;
                this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
                this.pictureBox1.MouseEnter += new System.EventHandler(this.pictureBox1_MouseEnter);
                this.pictureBox1.MouseLeave += new System.EventHandler(this.pictureBox1_MouseLeave);
                // 
                // txtOutput
                // 
                this.txtOutput.Location = new System.Drawing.Point(13, 39);
                this.txtOutput.Name = "txtOutput";
                this.txtOutput.Size = new System.Drawing.Size(400, 96);
                this.txtOutput.TabIndex = 5;
                this.txtOutput.Text = "";
                this.txtOutput.TextChanged += new System.EventHandler(this.txtOutput_TextChanged);
                // 
                // cboSearchSite
                // 
                this.cboSearchSite.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
                this.cboSearchSite.FormattingEnabled = true;
                this.cboSearchSite.Items.AddRange(new object[] {
                "임거 (imgur)",
                "셔터스톡 (shutterstock)",
                "언스플래시 (unsplash)",
                "텀블러 (Tumblr)",
                "직접입력 (Custom)"});
                this.cboSearchSite.Location = new System.Drawing.Point(13, 13);
                this.cboSearchSite.Name = "cboSearchSite";
                this.cboSearchSite.Size = new System.Drawing.Size(121, 20);
                this.cboSearchSite.TabIndex = 7;
                // 
                // btnSave
                // 
                this.btnSave.Location = new System.Drawing.Point(326, 12);
                this.btnSave.Name = "btnSave";
                this.btnSave.Size = new System.Drawing.Size(88, 23);
                this.btnSave.TabIndex = 8;
                this.btnSave.Text = "크롤링 시작";
                this.btnSave.UseVisualStyleBackColor = true;
                this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
                this.btnSave.KeyDown += new System.Windows.Forms.KeyEventHandler(this.btnSave_KeyDown);
                // 
                // txtKeyword
                // 
                this.txtKeyword.Location = new System.Drawing.Point(140, 13);
                this.txtKeyword.Name = "txtKeyword";
                this.txtKeyword.Size = new System.Drawing.Size(180, 21);
                this.txtKeyword.TabIndex = 9;
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(426, 303);
                this.Controls.Add(this.txtKeyword);
                this.Controls.Add(this.btnSave);
                this.Controls.Add(this.cboSearchSite);
                this.Controls.Add(this.pictureBox1);
                this.Controls.Add(this.txtOutput);
                this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
                this.MaximumSize = new System.Drawing.Size(442, 342);
                this.MinimumSize = new System.Drawing.Size(442, 342);
                this.Name = "Form1";
                this.Text = "엔지엠 매크로 - 무료 이미지 크롤러 v1.0";
                this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
                ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
                this.ResumeLayout(false);
                this.PerformLayout();
    
            }
    
            #endregion
    
            private System.Windows.Forms.PictureBox pictureBox1;
            private System.Windows.Forms.RichTextBox txtOutput;
            private System.Windows.Forms.ComboBox cboSearchSite;
            private System.Windows.Forms.Button btnSave;
            private System.Windows.Forms.TextBox txtKeyword;
        }
    }

     

     

    기존에 배포한 [ 이미지 크롤러 ]에는 없는 기능이 하나 추가되었어요. 기존에 임거, 셔터스톡, 언스플래시, 텀블러만 존재 했습니다. 혹시나 다른 사이트에서 이미지를 검색할수도 있어서 직접입력을 추가 했습니다. 얼마나 효용성이 있을지는 모르겠지만요.

                // 
                // cboSearchSite
                // 
                this.cboSearchSite.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
                this.cboSearchSite.FormattingEnabled = true;
                this.cboSearchSite.Items.AddRange(new object[] {
                "임거 (imgur)",
                "셔터스톡 (shutterstock)",
                "언스플래시 (unsplash)",
                "텀블러 (Tumblr)",
                "직접입력 (Custom)"});
                this.cboSearchSite.Location = new System.Drawing.Point(13, 13);
                this.cboSearchSite.Name = "cboSearchSite";
                this.cboSearchSite.Size = new System.Drawing.Size(121, 20);
                this.cboSearchSite.TabIndex = 7;

     

     

    아래는 실제 비즈니스 로직이 들어 있는 코드입니다. 코드가 단순하기 때문에 쉽게 분석이 가능할거예요.

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Windows.Forms;
    
    namespace ImageCrawler
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                txtOutput.AppendText("쉽고 안전한 엔지엠 매크로!");
                txtOutput.AppendText(Environment.NewLine);
                txtOutput.AppendText("http://ngmsoftware.com");
                txtOutput.AppendText(Environment.NewLine);
                txtOutput.AppendText("이 프로그램은 무료입니다^^");
                txtOutput.AppendText(Environment.NewLine);
    
                cboSearchSite.SelectedIndex = 0;
            }
    
            private void pictureBox1_MouseEnter(object sender, EventArgs e)
            {
                pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
                pictureBox1.Cursor = Cursors.Hand;
            }
    
            private void pictureBox1_MouseLeave(object sender, EventArgs e)
            {
                pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox1.Cursor = Cursors.Default;
            }
    
            private void pictureBox1_Click(object sender, EventArgs e)
            {
                Process.Start("http://ngmsoftware.com");
            }
    
            private void Form1_FormClosed(object sender, FormClosedEventArgs e)
            {
    #if !DEBUG
                Process.Start("http://ngmsoftware.com/bbs/board.php?bo_table=product_review");
    #endif
            }
    
            private void btnSave_Click(object sender, EventArgs e)
            {
                if (string.IsNullOrEmpty(txtKeyword.Text))
                {
                    MessageBox.Show(this, "검색할 이미지 키워드를 입력해야 합니다.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    txtKeyword.Focus();
                    return;
                }
    
                WebClient wc = new WebClient();
                HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
    
                try
                {
                    switch (cboSearchSite.SelectedIndex)
                    {
                        case 0:
                            htmlDocument.LoadHtml(wc.DownloadString($"https://imgur.com/search?q={txtKeyword.Text}"));
                            break;
                        case 1:
                            htmlDocument.LoadHtml(wc.DownloadString($"https://www.shutterstock.com/search/{txtKeyword.Text}"));
                            break;
                        case 2:
                            htmlDocument.LoadHtml(wc.DownloadString($"https://unsplash.com/s/photos/{txtKeyword.Text}"));
                            break;
                        case 3:
                            htmlDocument.LoadHtml(wc.DownloadString($"https://www.tumblr.com/search/{txtKeyword.Text}"));
                            break;
                        case 4:
                            htmlDocument.LoadHtml(wc.DownloadString($"{txtKeyword.Text}"));
                            break;
                    }
                }
                catch
                {
                    MessageBox.Show("이미지를 크롤링할 페이지가 유효하지 않습니다.");
                    return;
                }
    
                List<HtmlAgilityPack.HtmlNode> nodes = new List<HtmlAgilityPack.HtmlNode>();
                nodes.AddRange(htmlDocument.DocumentNode.GetElementsByTagName("body"));
    
                string savePath = Path.Combine(Application.StartupPath, "images");
                int fileName = 1;
                int successCnt = 0;
                int errorCnt = 0;
    
                foreach (HtmlAgilityPack.HtmlNode node in nodes)
                {
                    var urls = node
                        .Descendants("img")
                        .Select(evt => evt.GetAttributeValue("src", null))
                        .Where(s => !String.IsNullOrEmpty(s));
    
                    if (urls != null)
                    {
                        foreach (string url in urls)
                        {
                            try
                            {
                                if (!Directory.Exists(savePath))
                                    Directory.CreateDirectory(savePath);
    
                                string link = url;
                                if (!url.StartsWith("http"))
                                    link = "http:" + url;
    
                                new WebClient().DownloadFile(link, Path.Combine(savePath, $"{fileName}{Path.GetExtension(url)}"));
                                txtOutput.AppendText($"{fileName}: {Path.GetFileName(url)}");
                                txtOutput.AppendText(Environment.NewLine);
                                successCnt++;
                            }
                            catch (Exception ex)
                            {
                                txtOutput.AppendText(ex.Message);
                                txtOutput.AppendText(Environment.NewLine);
                                errorCnt++;
                            }
    
                            fileName++;
                        }
                    }
                }
    
                txtOutput.AppendText($"성공: {successCnt}, 실패: {errorCnt}");
                txtOutput.AppendText(Environment.NewLine);
    
                if (fileName > 0)
                {
                    if (MessageBox.Show(this, $"{cboSearchSite.Text}에서 [{txtKeyword.Text}] 이미지를 모두 저장했습니다. 저장된 폴더를 여시겠습니까?", "Information", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK)
                        Process.Start(Path.Combine(Application.StartupPath, "Images"));
                }
                else
                {
                    MessageBox.Show(this, $"{cboSearchSite.Text}에서 [{txtKeyword.Text}] 이미지를 찾을 수 없습니다.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
    
            private void txtOutput_TextChanged(object sender, EventArgs e)
            {
                txtOutput.SelectionStart = txtOutput.Text.Length;
                txtOutput.ScrollToCaret();
            }
    
            private void btnSave_KeyDown(object sender, KeyEventArgs e)
            {
                if (e.KeyCode == Keys.Enter)
                {
                    btnSave.PerformClick();
                }
            }
        }
    
        public static class HtmlNodeExtensions
        {
            public static IEnumerable<HtmlAgilityPack.HtmlNode> GetElementsByName(this HtmlAgilityPack.HtmlNode parent, string name)
            {
                return parent.Descendants().Where(node => node.Name == name);
            }
    
            public static IEnumerable<HtmlAgilityPack.HtmlNode> GetElementsByTagName(this HtmlAgilityPack.HtmlNode parent, string name)
            {
                return parent.Descendants(name);
            }
        }
    }

     

     

    아~ 하나 빼먹었네요-_-; [ HtmlAgilityPack ] 누겟 패키지를 설치해야 합니다.

    61vqPmC.png

     

     

    어질리티팩을 설치하면 현재 로딩된 페이지의 소스 코드를 가져와서 분석하고 노드를 확인할 수 있습니다. 여기서 한계점이 발생하는데요. 구글이나 네이버처럼 이미지 주소를 우회해서 맵핑하는 경우입니다. 기술적인 용어가 적절한지는 모르겠지만, 실제 이미지에서 우클릭 후 주소를 복사해보면 전체 이미지 링크가 아닌 가상 링크로 되어 있는걸 알 수 있습니다. 또한 암호화되어 있어서 해석도 불가능하죠. 대략 아래와 같습니다.

    data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxITEhUSEhIVFRUVFxcXFRcVFxUVFRUVFRUXFx...

     

    그래서 어질리티팩과 셀레니움을 같이 사용하는 방식으로 많이 만들고 있습니다. 셀레니움은 해당 이미지를 직접 저장할 수 있는 기능이 포함되어 있기 때문인데요. 사람이 직접 이미지에서 우클릭 후 저장하기를 누르는것과 같은 형식입니다. 다만, 목록의 경우 썸네일이 저장되는 방식입니다. 원본 이미지를 저장하려면 더 복잡해지죠^^; 아무튼~ 다음에는 이미지 크롤링 프로그램에 셀레니움을 추가해서 기능을 확장해보도록 할께요. 이미 만드신분은 소스 공유좀 해주세요^^

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.