黑苹果Lion安装手记

黑苹果去年就安装了10.6.6的,lion出来后,本本升级了,然后看着黑苹果就不大顺眼了,于是,想琢磨着黑苹果也升级到lion吧,花了两天时间,下了三个版本,最终安装成功。首先使用了从appstore里下载的原版的lion,恢复到U盘安装,失败!然后下载了本论坛上提供的懒兔v1.3版本(10.7.3),安装时提示System State [S0 s3 s4 s5] s3,找了一天的资料试了各种办法仍然无从解决,最后下了iATKOS L2 (Mac OS X Lion 10.7.2),外国人的版本试试说不定可以哦~(下载:http://bbs.pcbeta.com/viewthread-943958-1-2.html)
当然,开始并没有那么幸运,使用变色龙启动后,一选择iATKOS L2,几秒后直接重启,连错误都不带给的,慢慢试了几次,发现硬盘助手在写入的时候,提示过有错误,无奈只能把安装镜像在本本上恢复到U盘里再安装,这时也出现了大把的错误,通过打上ncpi=0×2000 USBBusFix=Yes,以及换掉不支持的ps2键盘为usb键盘,终于可以正常安装了。安装好后什么驱动都没有,从pcbeta上又分别下载了各个驱动,通过Kext Helper安装上了,然后试了下自动更新,经过一个多小时的更新,成功更新到最新的10.7.4版本,这次比较顺利,声卡也完美了,以前的10.6.6老是有噪声。发下我的电脑的硬件供参考:

BenQ台机,09年买的,现在大概属老爷机了,
cpu: intel core2 e7300 2.67G
主板:foxconn G31AX/G31MG/G31MV/G31MX Series
芯片组:Intel Bearlake G31
内存:2G*2
显卡:NVIDIA GeForce 8500GT
声卡:Realtek ALC662
网卡:Realtek PCIe GBE
安装成功后,使用了ps2的kext,我的ps2口的键盘也可以使用了,双显示器也很完美。

发几张照片欣赏下:

如何山寨一个百度文库

最近在做个网盘的应用,需要可以在线预览网盘里的文件,对于文本文件,图片,mp3等还算比较好解决,麻烦的是office类文档,pdf等,这些文件不可以在线预览,除非安装插件,而且IE会试图下载完成后才打开预览,这个项目无法接受,于是想到了百度文库的方法–使用flash在线预览office、pdf文档。于是也有了今天的文章,如何山寨一个百度文库。

flash预览office、pdf文档的原理还算简单,就是把所有的文件都转换成.swf格式文件,然后通过定制的flash播放器在线加载浏览。所以,我们的工作就分三步来实施:

1.FlexPaper(http://flexpaper.devaldi.com/) FlexPaper是一款开源的在线浏览swf文件应用,它的设计初衷就是用来在线查看pdf文件的。它的官网上提供了swc包,可以下载下来在flash里引用,然后再设计自己的flash输出打包即可。由于官网提供的swc包包含了大量的官网logo等信息,所以我下载它的源码包(http://code.google.com/p/flexpaper/source/checkout),使用svn工具checkout即可。源码包里包含三个目录,Example,FlexPaper,FlexPaper_SDK4,其中Example是示例,FlexPaper是基于flex sdk3.5的源码,FlexPaper_SDK4是flex 4.0的源码。我测试了下FlexPaper_SDK4里好像少了resource的swc文件,不能正常编译。于是只好用FlexPaper文件夹下的源码,也就是基于flex sdk 3.5来编译。编译过程不再多说,网上有很详细的教程,包括如何去掉官方的logo等。(去logo是违反开源协议的行为,请自行斟酌。)

 

2.转换office、pdf文件为可以在线浏览的swf文件。

首先我们来看pdf文件的转换:我们使用swftools(http://www.swftools.org/)这个工具,使用命令行,调用 pdf2swf -t 源文件.pdf -o 目标文件,即可,如果转换后有些文档不能浏览,需要再加上 -s flashversion=9强制生成新版的swf文件。swftools包含各个平台上的实现,使用*niux的服务器也可以在线转换。

再来看office文件的转换,office常用文件包括 word(doc,docx),excel(xls,xlsx),powerpoint(ppt,pptx)文件,我们只要实现这六种扩展名的文件(实际上只是三种)转换即可。目前可行的方案有以下几种:1)使用开源的open office来转换office文档为pdf,然后再转换生成的pdf为swf,open office由于是开源项目,对于复杂一点的office文档和新版本的office文档转换存在不少兼容问题,所以这个方向做为备选。2)office 2007后,office本身带有另存为pdf功能(office2007需要安装官方提供的topdf插件,office 2010已经内置这个功能),所以我们只需要在服务器上安装一个office 2012,即可调用office自行保存office文档为pdf,最后再调用pdftoswf转换为swf。使用office自带的保存pdf工具兼容性比较好,因此我们选择第二种方案。

当然,我们做的是网站,用户可能随时上传文件,我们不能在用户上传的web页面中调用转换命令,所以我们需要写个后台服务来完成这一工作。由于目前文档服务器是windows 2008,所以我们来写一个windows服务(有时间了我会给出python版本的转换代码,可用于linux服务器)。

windows服务我们采用C#来编写,基于.net 4.0。服务每30秒检查一下设定的web url,看是否有需要转换的文档,如果有就进行转换,服务器端采用php编写,代码比较简单,我就不列了,需要注意的是,由于pdf2swf在转换大文档的时候会消耗比较多的时间,服务器端代码一定要采用防并发的处理,并且服务的检查时间间隔不要太短,防止服务同时运行几个进程转换同一个文档。创建windows服务项目,部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    //抽象的转换父类
    public abstract class ConverterBase
    {
        public abstract bool Convert(string inputFilePath, string outputFilePath);
    }

    //office文档转换类
    public class OfficeConverter : ConverterBase
    {
        public override bool Convert(string inputFilePath, string outputFilePath)
        {
            var ext = Path.GetExtension(inputFilePath);
            switch (ext)
            {
                case ".doc":
                case ".docx":
                    return WordToPDF(inputFilePath, outputFilePath);
                    break;
                case ".xls":
                case ".xlsx":
                    return ExcelToPDF(inputFilePath, outputFilePath);
                    break;
                case ".ppt":
                case ".pptx":
                    return PptToPDF(inputFilePath, outputFilePath);
                    break;
                default:
                    break;
            }

            return true;
        }

        private bool WordToPDF(string sourcePath, string targetPath)
        {
            var result = false;
            const WdExportFormat exportFormat = WdExportFormat.wdExportFormatPDF;
            var paramMissing = Type.Missing;
            var wordApplication = new Microsoft.Office.Interop.Word.ApplicationClass();
            Document wordDocument = null;
            try
            {
                object paramSourceDocPath = sourcePath;
                var paramExportFilePath = targetPath;

                const WdExportFormat paramExportFormat = exportFormat;
                const bool paramOpenAfterExport = false;
                const WdExportOptimizeFor paramExportOptimizeFor = WdExportOptimizeFor.wdExportOptimizeForPrint;
                const WdExportRange paramExportRange = WdExportRange.wdExportAllDocument;
                const int paramStartPage = 0;
                const int paramEndPage = 0;
                const WdExportItem paramExportItem = WdExportItem.wdExportDocumentContent;
                const bool paramIncludeDocProps = true;
                const bool paramKeepIRM = true;
                const WdExportCreateBookmarks paramCreateBookmarks = WdExportCreateBookmarks.wdExportCreateWordBookmarks;
                const bool paramDocStructureTags = true;
                const bool paramBitmapMissingFonts = true;
                const bool paramUseISO19005_1 = false;

                wordDocument = wordApplication.Documents.Open(
                    ref paramSourceDocPath, ref paramMissing, ref paramMissing,
                    ref paramMissing, ref paramMissing, ref paramMissing,
                    ref paramMissing, ref paramMissing, ref paramMissing,
                    ref paramMissing, ref paramMissing, ref paramMissing,
                    ref paramMissing, ref paramMissing, ref paramMissing,
                    ref paramMissing);

                if (wordDocument != null)
                    wordDocument.ExportAsFixedFormat(paramExportFilePath,
                                                     paramExportFormat, paramOpenAfterExport,
                                                     paramExportOptimizeFor, paramExportRange, paramStartPage,
                                                     paramEndPage, paramExportItem, paramIncludeDocProps,
                                                     paramKeepIRM, paramCreateBookmarks, paramDocStructureTags,
                                                     paramBitmapMissingFonts, paramUseISO19005_1,
                                                     ref paramMissing);
                result = true;
            }
            catch
            {
                result = false;
            }
            finally
            {
                if (wordDocument != null)
                {
                    wordDocument.Close(ref paramMissing, ref paramMissing, ref paramMissing);
                    wordDocument = null;
                }
                if (wordApplication != null)
                {
                    wordApplication.Quit(ref paramMissing, ref paramMissing, ref paramMissing);
                    wordApplication = null;
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            return result;
        }

        private bool ExcelToPDF(string sourcePath, string targetPath)
        {
            var result = false;
            const XlFixedFormatType targetType = XlFixedFormatType.xlTypePDF;
            var missing = Type.Missing;
            Microsoft.Office.Interop.Excel.ApplicationClass application = null;
            Workbook workBook = null;
            try
            {
                application = new Microsoft.Office.Interop.Excel.ApplicationClass();
                object target = targetPath;
                object type = targetType;
                workBook = application.Workbooks.Open(sourcePath, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing);
                workBook.ExportAsFixedFormat(targetType, target, XlFixedFormatQuality.xlQualityStandard, true, false, missing, missing, missing, missing); result = true;
            }
            catch
            {
                result = false;
            }
            finally
            {
                if (workBook != null)
                {
                    workBook.Close(true, missing, missing);
                    workBook = null;
                }
                if (application != null)
                {
                    application.Quit();
                    application = null;
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            return result;
        }

        private bool PptToPDF(string sourcePath, string targetPath)
        {
            bool result;
            const PpSaveAsFileType targetFileType = PpSaveAsFileType.ppSaveAsPDF;
            var missing = Type.Missing;
            Microsoft.Office.Interop.PowerPoint.ApplicationClass application = null;
            Presentation persentation = null;
            try
            {
                application = new Microsoft.Office.Interop.PowerPoint.ApplicationClass();
                persentation = application.Presentations.Open(sourcePath, MsoTriState.msoTrue, MsoTriState.msoFalse, MsoTriState.msoFalse);
                persentation.SaveAs(targetPath, targetFileType, MsoTriState.msoTrue);
                result = true;
            }
            catch
            {
                result = false;
            }
            finally
            {
                if (persentation != null)
                {
                    persentation.Close();
                    persentation = null;
                }
                if (application != null)
                {
                    application.Quit();
                    application = null;
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            return result;
        }

    }

    //pdf转换swf
    public class SwfConverter : ConverterBase
    {
        public override bool Convert(string inputFilePath, string outputFilePath)
        {
            inputFilePath = inputFilePath.Replace('/', Path.DirectorySeparatorChar);
            var directory = Path.GetDirectoryName(outputFilePath);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
            using (var p = new Process())
            {
                p.StartInfo.WorkingDirectory = Config.SwftoolsPath;
                p.StartInfo.FileName = "pdf2swf";
                p.StartInfo.Arguments = string.Format("-t {0} -o {1} -s flashversion=9", inputFilePath, outputFilePath);

                p.Start();
                p.WaitForExit();
            }

            Helper.WriteLog("success convert!", "success");
            return true;
        }
    }

最后是windows服务主文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    public class Config
    {
        static Config()
        {
            //是否在转换中
            IsDoing = false;
            //请求需要转换的频率,目前设置30秒检查一次,不可太小,否则服务器会同时进行大量的转换,严重影响服务器的性能。pdftoswf很耗系统资源,特别是cpu
            CheckInterval = Convert.ToInt32(ConfigurationManager.AppSettings["checkInterval"]);
            //swftools在服务器上的安装目录
            SwftoolsPath = ConfigurationManager.AppSettings["swftoolsPath"];
            //请求需要转换的文档的url
            GetUrl = ConfigurationManager.AppSettings["getUrl"];
            //转换成功通过web服务器的url
            UpdateUrl = ConfigurationManager.AppSettings["updateUrl"];
            //转换文档所在目录
            UploadPath = ConfigurationManager.AppSettings["uploadPath"];
            //md5 key,用于url加密,防止别人恶意调用
            Md5Key = ConfigurationManager.AppSettings["md5Key"];
        }

        public static int CheckInterval { get; set; }
        public static string SwftoolsPath { get; set; }
        public static bool IsDoing { get; set; }
        public static string GetUrl { get; set; }
        public static string UpdateUrl { get; set; }
        public static string UploadPath { get; set; }
        public static string Md5Key { get; set; }
    }

    public class EzService : ServiceBase
    {
        private Timer timer;


        public EzService()
        {
            InitializeComponent();

            timer = new Timer(callback, 0, 0, Config.CheckInterval * 1000);
        }

        protected override void OnStart(string[] args)
        {

        }

        protected override void OnStop()
        {
        }

        private void callback(Object obj)
        {
            if (!Config.IsDoing)
            {
                DoWork();
            }
        }

        private void DoWork()
        {
            Config.IsDoing = true;

            var remoteResult = Helper.GetOne(Config.GetUrl, 1);
            Helper.WriteLog(remoteResult, "remote");
            var remoteObj = new { id = 0, filePath = string.Empty, ext = string.Empty };

            if (!string.IsNullOrEmpty(remoteResult))
            {
                try
                {
                    var obj = JsonConvert.DeserializeAnonymousType(remoteResult, remoteObj);
                    if (obj != null && obj.id > 0)
                    {
                        var convertFilePath = Path.Combine(Config.UploadPath, obj.filePath);
                        var convertedFilePath = Path.GetDirectoryName(convertFilePath).Replace("netdisk", "netdiskc");//转换成功的文档保存到另一个目录里
                        var convertedFileName = Path.GetFileNameWithoutExtension(convertFilePath) + ".swf";
                        var convertedFile = Path.Combine(convertedFilePath, convertedFileName);

                        var convertFileExt = Path.GetExtension(convertFilePath).ToLower();

                        //convert doc file to pdf
                        var docFileExtList = new List<string> { ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx" };
                        if (docFileExtList.Contains(convertFileExt))
                        {
                            var pdfFileName = Path.GetFileNameWithoutExtension(convertFilePath) + ".pdf";
                            var pdfFilePath = Path.Combine(convertedFilePath, pdfFileName);
                            var docConverter = new OfficeConverter();
                            docConverter.Convert(convertFilePath, pdfFilePath);
                            convertFilePath = pdfFilePath;
                        }

                        var converter = new SwfConverter();
                        var convertResult = converter.Convert(convertFilePath, convertedFile);

                        if (convertResult)
                        {
                            while (true)
                            {
                                var ret = Helper.Update(Config.UpdateUrl, obj.id);
                                if (!ret)
                                {
                                    Thread.Sleep(3000);//更新转换成功的文档,每3秒更新一次,直到成功为止,防止web服务器掉线没更新导致重复转换
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Helper.WriteLog(ex.Message, "error");
                }
            }

            Config.IsDoing = false;
        }
    }

将服务的运行帐户设为local system,使用installutil安装此windows服务后,测试了下,在把windows服务作为console应用调试的状态下,转换每次都是成功的,但是一旦变成windows服务,转换即永远结束不了,可以看到系统调用了winword.exe,但是并不会结束这个进程,要转换的文件也一直没有出现,这是很奇怪的现象。
使用windows服务+office来检索google,发现了微软关于office用于windows服务的声明:基本上就是说office并没有经过严格的测试用于长时间无人值守的windows服务程序,微软也不建议把office用在windows服务里,看来微软是一推干净。有人说需要设置windows服务为允许桌面交互,试了下,也不行。最后在台湾某牛人博客上看到,windows服务调用office需要设置office dcom组件的属性。方法如下:开始,运行,输入dcomcnfg.exe,打开的窗口里,找到组件服务-计算机-我的电脑-DCOM配置,找到Microsoft Word 97 – 2003文档,右键,属性-标识,将选择运行此应用程序的用户帐户改为“交互式用户”,确定即可。excel的名称为Microsoft Excel Application,ppt的为Microsoft Powerpoint 幻灯片,分别修改为些属性即可。

3.最后是在页面里嵌入flexpaper的swf文档,并且载入需要浏览的转换好的文档,代码如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<script src="http://s.ezn.cc/js/flexpaper/swfobject.js" type="text/javascript"></script>
<script src="http://s.ezn.cc/js/flexpaper/flexpaper_flash.js" type="text/javascript"></script>
<div class="wrap10">
    <div class="sep10"></div>
    <div class="formWrapper">
        <ul class="css-tabs" id="tabs">
            <li><a class="current" href="javascript:;">预览《电脑爱好者》2012年第04期.pdf</a></li>
        </ul>
        <div class="css-panes">
            <div class="item">
                <div class="sep10"></div>
                <object width="850" height="400" type="application/x-shockwave-flash" id="FlexPaperViewer" name="FlexPaperViewer" data="http://s.ezn.cc/flash/EzDocPlayer.swf"><param name="quality" value="high"><param name="bgcolor" value="#ffffff"><param name="allowscriptaccess" value="always"><param name="allowfullscreen" value="true"><param name="flashvars" value="SwfFile=http%3A//up.ezn.cc/netdiskc/2012/04/14/7SeE9uNu.swf&amp;Scale=1&amp;ZoomTransition=easeOut&amp;ZoomTime=0.5&amp;ZoomInterval=0.1&amp;FitPageOnLoad=false&amp;FitWidthOnLoad=true&amp;PrintEnabled=true&amp;FullScreenAsMaxWindow=false&amp;ProgressiveLoading=false&amp;PrintToolsVisible=true&amp;ViewModeToolsVisible=true&amp;ZoomToolsVisible=true&amp;FullScreenVisible=true&amp;NavToolsVisible=true&amp;CursorToolsVisible=true&amp;SearchToolsVisible=true&amp;localeChain=zh_CN"></object>
            </div>
        </div>

    </div>
</div>
<script type="text/javascript">
    var swfFile = escape("http://up.ezn.cc/netdiskc/2012/04/14/7SeE9uNu.swf");
</script>

<script type="text/javascript">
    &lt;!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. --&gt;
    var swfVersionStr = "9.0.124";
    &lt;!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. --&gt;
    var xiSwfUrlStr = "${expressInstallSwf}";
    var flashvars = {
        SwfFile : swfFile,
        Scale : 1.0,
        ZoomTransition : "easeOut",
        ZoomTime : 0.5,
        ZoomInterval : 0.1,
        FitPageOnLoad : false,
        FitWidthOnLoad : true,
        PrintEnabled : true,
        FullScreenAsMaxWindow : false,
        ProgressiveLoading : false,

        PrintToolsVisible : true,
        ViewModeToolsVisible : true,
        ZoomToolsVisible : true,
        FullScreenVisible : true,
        NavToolsVisible : true,
        CursorToolsVisible : true,
        SearchToolsVisible : true,

        localeChain: "zh_CN"
    };
    var params = {

    }
    params.quality = "high";
    params.bgcolor = "#ffffff";
    params.allowscriptaccess = "always";
    params.allowfullscreen = "true";
    var attributes = {};
    attributes.id = "FlexPaperViewer";
    attributes.name = "FlexPaperViewer";
    swfobject.embedSWF(
            "http://s.ezn.cc/flash/EzDocPlayer.swf", "flashContent",
            "850", "400",
            swfVersionStr, xiSwfUrlStr,
            flashvars, params, attributes);
    swfobject.createCSS("#flashContent", "display:block;text-align:left;");
</script>




至此,一个简易的在线office,pdf文件浏览的项目就完成了,配合一些服务器端的网站代码,即可实现百度文库的功能。

the last,thanks.

ps.

今天在服务器上部署,windows 2008 r2,突然发现DCOM配置里找不到WORD,EXCEL,POWERPOINT的配置项,GOOGLE了一下,原来是64位系统的问题,做如下操作即可:
由於Dcomcnfg是一個32位的配置,在64系統下并不能被完全支持

在WIN2008 X64系統中,可以通過如下方式進入DCOM配置

運行:mmc -32

然後會彈出一個程序,工菜單中選擇File->Add/Remove Snap

添加Component Services,然後在Component Services下找到Excel後再進行配置,就和32位系統一樣的了;

C#抓取京东商城的书信息

最近在做个在线的小型图书管理软件,需要考虑用户在添加书籍的时候,能自动通过书的isbn编码填充书的其它介绍内容,网上搜了下isbn数据库,没有现成的可以下载使用的。豆瓣提供了根据isbn查询书的内容,我试了下,能获取到的书的内容比较简单,基本只有书名,作者,出版社等简单信息,我想获取到的是类似在线商城里的那种完整的书籍信息,包括“编辑推荐”,“目录”,“内容介绍”什么的,这样只能自己手工抓取在线商城的书的信息了。

根据我的了解,大型的商城里数据大多比较难抓取,果然不出我所料,首先尝试的当当网就做了很好的防抓取,它采用了ajax获取书的详细描述信息,而且传输的文字还经过了编码,这样模拟浏览器下载下来的书籍浏览页面就不包含书的具体信息,而且根据测试,也没有特别好的办法让.net 的webclient很好地处理ajax请求。接着试了amazon.cn,发现也同样,首先它的url特别地复杂,另外它的书籍列表页面也是ajax获取的,这样抓取列表比较麻烦,算了,放弃。接着查看了京东,嗯,不错,还算厚道,理论上可以抓取,试了下另外一个china-pub,应该也可以抓取,好,开工。

首先看一下京东的书籍列表页和书籍详细信息页面:

以前也做过网页抓取信息的,不过都是简单的抓取个下载链接,图片什么的,使用的是正则表达式,没有这么大面积地抓取页面上这么具体的信息,页面比较复杂,大量的css,js,用正则匹配这么多数据效率不高,而且很容易出错。于是请出今天的主角:Html Agility Pack(http://htmlagilitypack.codeplex.com/),这个类库可以读取html,并把它解析成dom树,并可通过xpath和xslt解析,有点类似浏览器的解析,用它来做网络爬虫那是再合适不过了。关于xpath语法,请参照http://www.w3schools.com/xpath/

下面给出京东商城的抓取某个书分类的代码,代码比较简单,大部分就是拼字符串,也没考虑效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
public class Program
    {
        private static string savePath = System.Environment.CurrentDirectory + Path.DirectorySeparatorChar + "down\\list";
        private static string saveBookPath = System.Environment.CurrentDirectory + Path.DirectorySeparatorChar + "down\\book";
        private static string saveCoverPath = Path.Combine(saveBookPath, @"cover");
        private static int count = 0;
        static void Main(string[] args)
        {
            //依次运行下面的三个方法。1 下载列表页,2 解析列表页 3 抓取书的信息
            //DownloadList();
            //ParseList();

            ParseBook();

            Console.Read();
        }

        static void ParseBook()
        {
            var minPage = 1;
            var maxPage = 496;
            for (var page = minPage; page <= maxPage; page++)
            {
                var bookPath = string.Format("{0}\\book_{1}.html", saveBookPath, page);
                ParseBookPage(bookPath);
                Thread.Sleep(3000*1);
            }
            Console.WriteLine("finishing all works!");
        }

        static void ParseBookPage(string bookPath)
        {
            var bookInfo = new BookInfo();
            var doc = new HtmlDocument();
            doc.Load(bookPath);

            var rootNode = doc.GetElementbyId("book");

            #region bookname
            //book name
            var bookNameNode = rootNode.SelectSingleNode("//div[@id='name']//h1[1]");
            if (bookNameNode != null && bookNameNode.InnerText != null)
            {
                bookInfo.BookName = bookNameNode.InnerText.Trim();
            }

            #endregion

           
            #region price
            //price
            var priceNode = rootNode.SelectSingleNode("//ul[@id='book-price']//li[1]");
            var priceText = priceNode.InnerText.Replace("\r\n", "").Trim().Replace("&nbsp;","").Replace("定价:¥","");
            bookInfo.Price = priceText;
            #endregion
           
            #region img
            //img
            var imgNode = rootNode.SelectSingleNode("//div[@class='jqzoom']//img[1]/@src");
            var img = imgNode.Attributes["src"].Value;
            var imgPath = DateTime.Now.ToShortDateString().Replace("/",@"\");
            var cover = "cover/" + imgPath.Replace(@"\","/");
            imgPath = Path.Combine(saveCoverPath, imgPath);
            if (!Directory.Exists(imgPath))
            {
                Directory.CreateDirectory(imgPath);
            }
            var rand = DateTime.Now.Ticks.ToString() + new Random().Next(1000, 9999).ToString();
            var randomName = Helper.GetMd5Hash(rand);
            imgPath = Path.Combine(imgPath, randomName + ".jpg");
            cover = cover + "/" + randomName + ".jpg";
            DownloadPage(img, imgPath);
            bookInfo.Cover = cover;
            #endregion
           
            #region basic items
            var basicItemNodes = rootNode.SelectNodes("//ul[@id='summary']//li");
           
            foreach(var item in basicItemNodes)
            {
                var text = item.InnerText;
                text = text.Replace("\r\n\t\t\t\t", "").Replace("&nbsp;", "").Replace("\t\t\t\t", "").Trim();
                var arr = text.Split(':');
                var key = arr[0];
                if (arr.Length > 1)
                {
                    var value = arr[1];

                    if (key == "作  者")
                    {
                        var keyArr = value.Split('著');
                        bookInfo.Author = keyArr[0].Trim();
                        if (keyArr.Length > 1)
                        {
                            bookInfo.Translator = keyArr[1].Trim();
                        }
                    }
                    if (key == "出 版 社")
                    {
                        bookInfo.Press = value;
                    }
                    if (key == "ISBN")
                    {
                        bookInfo.Isbn = value;
                    }
                    if (key == "出版时间")
                    {
                        bookInfo.PressDate = value;
                    }
                    if (key == "开  本")
                    {
                        bookInfo.BookSize = value;
                    }
                    if(key == "装  帧")
                    {
                        bookInfo.Package = value;
                    }
                    if (key == "页  数")
                    {
                        var pages = 0;
                        Int32.TryParse(value,out pages);
                        bookInfo.Pages = Convert.ToInt32(pages);
                    }
                    if (key == "版  次")
                    {
                        var keyArr = value.Split('-');
                        if (keyArr.Length > 0)
                        {
                            bookInfo.EditionNum = keyArr[0];
                            if (keyArr.Length > 1)
                            {
                                bookInfo.PressNum = keyArr[1];
                            }
                        }
                    }
                }
            }
#endregion

            #region content
            var contentNodes = rootNode.SelectNodes("//div[@class='m m1']");
            var contentText = string.Empty;
            if (contentNodes != null)
            {
                foreach (var contentNode in contentNodes)
                {
                    var removeNode = contentNode.SelectSingleNode("div[@class='mt']");
                    if(removeNode.InnerText != "" && removeNode.InnerText.IndexOf("该作者其它作品") != -1)
                    {
                        break;
                    }
                    var c = contentNode.InnerHtml.Replace("\r\n", "").Replace("\t", "");
                    c = c.Replace("<ul class=\"list-h\">                        <li>·<a href='http://book.360buy.com/10797010.html' title=\"《人脉是设计出来的》知人性攻人心,最低价!\">《人脉是设计出来的》知人性攻人心,最低价! &gt;&gt;</a></li><li>·<a href='http://book.360buy.com/10901270.html' title=\"《那些年,我们一起追的女孩》65折!\">《那些年,我们一起追的女孩》65折! &gt;&gt;</a></li></ul>", "");
                    c = c.Replace("<div class=\"more\"><a href=\"javascript:void(0)\">·查看全部&gt;&gt;</a></div>",
                                              "");
                    c = c.Replace("<div class=\"more\" style=\"display: block;\"><a href=\"javascript:void(0)\">·查看全部&gt;&gt;</a></div>", "");
                    contentText += c;
                }
                bookInfo.Content = contentText;
            }

            #endregion

            count++;
            SaveToDb(bookInfo);
            Console.WriteLine("finishing parsing {0}...",count);

        }

        static void ParseList()
        {
            var minPage = 1;
            var maxPage = 34;

            for (var page = minPage; page <= maxPage; page++)
            {
                var pagePath = string.Format("{0}\\list_{1}.html", savePath, page);
                ParsePage(pagePath);
            }

            Console.WriteLine("finishing download pages,total: {0}...", count);
        }

        static void ParsePage(string pagePath)
        {
            var doc = new HtmlDocument();
            doc.Load(pagePath);
            var rootNode = doc.GetElementbyId("plist");

            var bookItemNodes = rootNode.SelectNodes("//dt[@class='p-name']/a");
            foreach (var bookItem in bookItemNodes)
            {
                //var title = bookItem.SelectSingleNode("//a[@class='blue14a']");
                count++;
                var linkStr = bookItem.OuterHtml;
                var linkHref = string.Empty;

                Regex reg = new Regex(@"(?is)<a[^>]*?href=(['""\s]?)(?<href>[^'""\s]*)\1[^>]*?>");
                MatchCollection match = reg.Matches(linkStr);
                foreach (Match m in match)
                {
                    linkHref = m.Groups["href"].Value;
                }

                var info = new BookTitleInfo { id = count, title = bookItem.InnerText, link = linkHref };
                //SaveToDb(info);
                DownloadPage(linkHref, string.Format("{0}\\book_{1}.html", saveBookPath, count));
                Console.WriteLine("downloading book {0}...", count);
                Thread.Sleep(1000);
            }
        }

        static void SaveToDb(BookTitleInfo info)
        {
            var connString = "Server=127.0.0.1;Uid=root;Pwd=pwd;Database=db;CharSet=utf8";
            MySqlConnection conn;
            MySqlCommand cmd;

            conn = new MySqlConnection { ConnectionString = connString };
            conn.Open();
            cmd = new MySqlCommand
            {
                Connection = conn,
                CommandText =
                    "insert into `booktitle`(bid,title,link) values(@id,@title,@link)"
            };

            cmd.Prepare();
            cmd.Parameters.Add("@id", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@title", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@link", MySqlDbType.VarChar, 255);

            cmd.Parameters["@id"].Value = info.id;
            cmd.Parameters["@title"].Value = info.title;
            cmd.Parameters["@link"].Value = info.link;

            cmd.ExecuteNonQuery();
        }

        static void SaveToDb(BookInfo info)
        {
            if(string.IsNullOrEmpty(info.Author))
            {
                info.Author = "";
            }
            if(string.IsNullOrEmpty(info.BookName))
            {
                info.BookName = "";
            }
            if(string.IsNullOrEmpty(info.BookSize))
            {
                info.BookSize = "";
            }
            if(string.IsNullOrEmpty(info.Content))
            {
                info.Content = "";
            }
            if(string.IsNullOrEmpty(info.Cover))
            {
                info.Cover = "";
            }
            if(string.IsNullOrEmpty(info.EditionNum))
            {
                info.EditionNum = "";
            }
            if(string.IsNullOrEmpty(info.Isbn))
            {
                info.Isbn = "";
            }
            if(string.IsNullOrEmpty(info.Language))
            {
                info.Language = "";
            }
            if(string.IsNullOrEmpty(info.Package))
            {
                info.Package = "";
            }
            if(string.IsNullOrEmpty(info.Paper))
            {
                info.Paper = "";
            }
            if(string.IsNullOrEmpty(info.Press))
            {
                info.Press = "";
            }
            if(string.IsNullOrEmpty(info.PressDate))
            {
                info.PressDate = "";
            }
            if(string.IsNullOrEmpty(info.PressNum))
            {
                info.PressNum = "";
            }
            if(string.IsNullOrEmpty(info.Price))
            {
                info.Price = "";
            }
            if(string.IsNullOrEmpty(info.Translator))
            {
                info.Translator = "";
            }

            var connString = "Server=127.0.0.1;Uid=root;Pwd=pwd;Database=db;CharSet=utf8";
            MySqlConnection conn;
            MySqlCommand cmd;

            conn = new MySqlConnection { ConnectionString = connString };
            conn.Open();
            cmd = new MySqlCommand
            {
                Connection = conn,
                CommandText =
                    "insert into `ezdata_book_pendding`(isbn,bookName,author,translator,price,language,press,pressDate,editionNum,pressNum,package,paper,bookSize,pages,words,content,dateline,status,cover)" +
                    " values(@isbn,@bookName,@author,@translator,@price,@language,@press,@pressDate,@editionNum,@pressNum,@package,@paper,@bookSize,@pages,@words,@content,@dateline,@status,@cover)"
            };

            cmd.Prepare();
            cmd.Parameters.Add("@isbn", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@bookName", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@author", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@translator", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@price", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@language", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@press", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pressDate", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@editionNum", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pressNum", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@package", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@paper", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@bookSize", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pages", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@words", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@content", MySqlDbType.String, 999999);
            cmd.Parameters.Add("@dateline", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@status", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@cover", MySqlDbType.VarChar, 255);

            cmd.Parameters["@isbn"].Value = info.Isbn;
            cmd.Parameters["@bookName"].Value = info.BookName;
            cmd.Parameters["@author"].Value = info.Author;
            cmd.Parameters["@translator"].Value = info.Translator;
            cmd.Parameters["@price"].Value = info.Price;
            cmd.Parameters["@language"].Value = info.Language;
            cmd.Parameters["@press"].Value = info.Press;
            cmd.Parameters["@pressDate"].Value = info.PressDate;
            cmd.Parameters["@editionNum"].Value = info.EditionNum;
            cmd.Parameters["@pressNum"].Value = info.PressNum;
            cmd.Parameters["@package"].Value = info.Package;
            cmd.Parameters["@paper"].Value = info.Paper;
            cmd.Parameters["@bookSize"].Value = info.BookSize;
            cmd.Parameters["@pages"].Value = info.Pages;
            cmd.Parameters["@words"].Value = info.Words;
            cmd.Parameters["@content"].Value = info.Content;
            var timeStamp = new DateTime(1970, 1, 1);
            var a = (DateTime.Now.Ticks - timeStamp.Ticks)/10000000;
            cmd.Parameters["@dateline"].Value = a;
            cmd.Parameters["@status"].Value = 1;
            cmd.Parameters["@cover"].Value = info.Cover;
           
            cmd.ExecuteNonQuery();
        }

        static void DownloadList()
        {
            var minPage = 1;
            var maxPage = 34;
            var urlTemplate = "http://www.360buy.com/products/1713-3278-3638-0-0-0-0-0-0-0-1-1-{0}.html";
            var saveFile = string.Empty;
            for (var page = minPage; page <= maxPage; page++)
            {
                saveFile = string.Format("{0}\\list_{1}.html", savePath, page);
                DownloadPage(string.Format(urlTemplate, page), saveFile);
                Thread.Sleep(3000);
                Log(string.Format("downloading list-page {0}", page));
            }
            Console.WriteLine("finishing download list {0}...", count);
        }

        static void DownloadPage(string pageUrl, string saveFile)
        {
            var uri = new Uri(pageUrl);

            using (var wc = new CGWebClient())
            {
                wc.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                try
                {
                    wc.DownloadFile(pageUrl, saveFile);
                }catch(Exception ex)
                {
                    Log(ex.Message);
                }
            }
        }

        static void Log(string str)
        {
            Console.WriteLine(str);
            using (var f = File.AppendText("log.txt"))
            {
                f.WriteLine(str);
            }
        }
}

效果图:




另附上 china-pub 抓取的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
public class Program
    {
        private static string savePath = System.Environment.CurrentDirectory + Path.DirectorySeparatorChar + "down\\list";
        private static string saveBookPath = System.Environment.CurrentDirectory + Path.DirectorySeparatorChar + "down\\book";
        private static string saveCoverPath = Path.Combine(saveBookPath, @"cover");
        private static int count = 0;
        static void Main(string[] args)
        {
            //DownloadList();
            //ParseList();

            ParseBook();

            Console.Read();
        }

        static void ParseBook()
        {
            var minPage = 1;
            var maxPage = 400;
            for (var page = minPage; page <= maxPage; page++)
            {
                var bookPath = string.Format("{0}\\book_{1}.html", saveBookPath, page);
                ParseBookPage(bookPath);
                //Thread.Sleep(1000*1);
            }
            Console.WriteLine("finishing all works!");
        }

        static void ParseBookPage(string bookPath)
        {
            var bookInfo = new BookInfo();
            var doc = new HtmlDocument();
            doc.Load(bookPath);

            var rootNode = doc.GetElementbyId("main");

            #region bookname
            //book name
            var bookNameNode = rootNode.SelectSingleNode("//h1[@class='black15c']");
            if (bookNameNode != null && bookNameNode.InnerText != null)
            {
                bookInfo.BookName = bookNameNode.InnerText;
            }

            #endregion

            #region price
            //price
            var priceNode = rootNode.SelectSingleNode("//div[@class='price-area']//li[1]");
            var priceText = priceNode.InnerText.Replace("\r\n", "").Trim().Replace("定价 :¥","");
            bookInfo.Price = priceText;
            #endregion

            #region img
            //img
            var imgNode = rootNode.SelectSingleNode("//div[@class='info-book-left']//img[1]/@src");
            var img = imgNode.Attributes["src"].Value;
            var imgPath = DateTime.Now.ToShortDateString().Replace("/",@"\");
            var cover = "cover/" + imgPath.Replace(@"\","/");
            imgPath = Path.Combine(saveCoverPath, imgPath);
            if (!Directory.Exists(imgPath))
            {
                Directory.CreateDirectory(imgPath);
            }
            var randomName = Helper.GetMd5Hash(count.ToString());
            imgPath = Path.Combine(imgPath, randomName + ".jpg");
            cover = cover + "/" + randomName + ".jpg";
            //DownloadPage(img, imgPath);
            bookInfo.Cover = cover;
            #endregion

            #region basic items
            var basicItemNodes = rootNode.SelectNodes("//div[@class='lcon more-infos']//li");
           
            foreach(var item in basicItemNodes)
            {
                var text = item.InnerText;
                text = text.Replace("\r\n\r\n", "").Replace("&nbsp;","").Replace("[作译者介绍]","");
                var arr = text.Split(':');
                var key = arr[0];
                if (arr.Length > 1)
                {
                    var value = arr[1];

                    if (key == "作者")
                    {
                        var keyArr = value.Split(',');
                        bookInfo.Author = keyArr[0];
                        if (keyArr.Length > 1)
                        {
                            bookInfo.Translator = keyArr[1];
                        }
                    }
                    if (key == "出版社")
                    {
                        bookInfo.Press = value;
                    }
                    if (key == "ISBN")
                    {
                        bookInfo.Isbn = value;
                    }
                    if (key == "出版日期")
                    {
                        bookInfo.PressDate = value;
                    }
                    if (key == "开本")
                    {
                        bookInfo.BookSize = value;
                    }
                    if (key == "页码")
                    {
                        bookInfo.Pages = Convert.ToInt32(value);
                    }
                    if (key == "版次")
                    {
                        var keyArr = value.Split('-');
                        if (keyArr.Length > 0)
                        {
                            bookInfo.EditionNum = keyArr[0];
                            if (keyArr.Length > 1)
                            {
                                bookInfo.PressNum = keyArr[1];
                            }
                        }
                    }
                }
            }
#endregion

            #region recommend
            var recommendNode = rootNode.SelectSingleNode("//div[@class='mcon commend-box']");
            var recommendText = string.Empty;
            if (recommendNode != null)
            {
                recommendText = recommendNode.InnerHtml.Replace("\r\n", "");
                recommendText = recommendText.Replace("<span class=\"bl\"></span><span class=\"br\"></span>", "");
            }

            #endregion

            #region content
            var contentNode = rootNode.SelectSingleNode("//div[@class='margin13']");
            var contentText = "";
            if (contentNode != null)
            {
                contentText = contentNode.InnerHtml.Replace("\r\n", "");
                contentText = contentText.Replace("<span><a href=\"#\">回到顶部↑</a></span>", "");
                contentText = contentText.Replace(" style=\"display:none;\"", "");
                contentText =
                    contentText.Replace(
                        "<div class=\"read-more\"><a href=\"#ml\" id=\"ml_txt11\" onclick='showhidediv(\"ml_txt\",\"ml_txt11\");'>↓展开全部内容</a></div>",
                        "");
                contentText = contentText.Replace("<h4><a href=\"#\">本书提供作译者介绍</a></h4>", "");
                contentText = contentText.Replace("<span class=\"brown\">&lt;&lt; </span>", "");
                contentText = Regex.Replace(contentText, "<a href='(.*)' class=\"brown12\">查看详细</a>", "");

                contentText = string.Format("{0}{1}", recommendText, contentText);
                bookInfo.Content = contentText;
            }

            #endregion

            count++;
            SaveToDb(bookInfo);
            Console.WriteLine("finishing parsing {0}...",count);

        }

        static void ParseList()
        {
            var minPage = 1;
            var maxPage = 20;
            for (var page = minPage; page <= maxPage; page++)
            {
                var pagePath = string.Format("{0}\\list_{1}.html", savePath, page);
                ParsePage(pagePath);
            }
        }

        static void ParsePage(string pagePath)
        {
            var doc = new HtmlDocument();
            doc.Load(pagePath);
            var rootNode = doc.GetElementbyId("xseo");

            var bookItemNodes = rootNode.SelectNodes("//a[@class='blue14a']");
            foreach (var bookItem in bookItemNodes)
            {
                //var title = bookItem.SelectSingleNode("//a[@class='blue14a']");
                count++;
                var linkStr = bookItem.OuterHtml;
                var linkHref = string.Empty;

                Regex reg = new Regex(@"(?is)<a[^>]*?href=(['""\s]?)(?<href>[^'""\s]*)\1[^>]*?>");
                MatchCollection match = reg.Matches(linkStr);
                foreach (Match m in match)
                {
                    linkHref = m.Groups["href"].Value;
                }

                var info = new BookTitleInfo { id = count, title = bookItem.InnerText, link = linkHref };
                //SaveToDb(info);
                DownloadPage(linkHref, string.Format("{0}\\book_{1}.html", saveBookPath, count));
                Thread.Sleep(1000);
            }
            Console.WriteLine("count={0}", count);
        }

        static void SaveToDb(BookTitleInfo info)
        {
            var connString = "Server=127.0.0.1;Uid=root;Pwd=pwd;Database=db;CharSet=utf8";
            MySqlConnection conn;
            MySqlCommand cmd;

            conn = new MySqlConnection { ConnectionString = connString };
            conn.Open();
            cmd = new MySqlCommand
            {
                Connection = conn,
                CommandText =
                    "insert into `booktitle`(bid,title,link) values(@id,@title,@link)"
            };

            cmd.Prepare();
            cmd.Parameters.Add("@id", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@title", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@link", MySqlDbType.VarChar, 255);

            cmd.Parameters["@id"].Value = info.id;
            cmd.Parameters["@title"].Value = info.title;
            cmd.Parameters["@link"].Value = info.link;

            cmd.ExecuteNonQuery();
        }

        static void SaveToDb(BookInfo info)
        {
            if(string.IsNullOrEmpty(info.Author))
            {
                info.Author = "";
            }
            if(string.IsNullOrEmpty(info.BookName))
            {
                info.BookName = "";
            }
            if(string.IsNullOrEmpty(info.BookSize))
            {
                info.BookSize = "";
            }
            if(string.IsNullOrEmpty(info.Content))
            {
                info.Content = "";
            }
            if(string.IsNullOrEmpty(info.Cover))
            {
                info.Cover = "";
            }
            if(string.IsNullOrEmpty(info.EditionNum))
            {
                info.EditionNum = "";
            }
            if(string.IsNullOrEmpty(info.Isbn))
            {
                info.Isbn = "";
            }
            if(string.IsNullOrEmpty(info.Language))
            {
                info.Language = "";
            }
            if(string.IsNullOrEmpty(info.Package))
            {
                info.Package = "";
            }
            if(string.IsNullOrEmpty(info.Paper))
            {
                info.Paper = "";
            }
            if(string.IsNullOrEmpty(info.Press))
            {
                info.Press = "";
            }
            if(string.IsNullOrEmpty(info.PressDate))
            {
                info.PressDate = "";
            }
            if(string.IsNullOrEmpty(info.PressNum))
            {
                info.PressNum = "";
            }
            if(string.IsNullOrEmpty(info.Price))
            {
                info.Price = "";
            }
            if(string.IsNullOrEmpty(info.Translator))
            {
                info.Translator = "";
            }

            var connString = "Server=127.0.0.1;Uid=root;Pwd=pwd;Database=db;CharSet=utf8";
            MySqlConnection conn;
            MySqlCommand cmd;

            conn = new MySqlConnection { ConnectionString = connString };
            conn.Open();
            cmd = new MySqlCommand
            {
                Connection = conn,
                CommandText =
                    "insert into `ezdata_book_pendding`(isbn,bookName,author,translator,price,language,press,pressDate,editionNum,pressNum,package,paper,bookSize,pages,words,content,dateline,status,cover)" +
                    " values(@isbn,@bookName,@author,@translator,@price,@language,@press,@pressDate,@editionNum,@pressNum,@package,@paper,@bookSize,@pages,@words,@content,@dateline,@status,@cover)"
            };

            cmd.Prepare();
            cmd.Parameters.Add("@isbn", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@bookName", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@author", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@translator", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@price", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@language", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@press", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pressDate", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@editionNum", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pressNum", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@package", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@paper", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@bookSize", MySqlDbType.VarChar, 255);
            cmd.Parameters.Add("@pages", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@words", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@content", MySqlDbType.String, 999999);
            cmd.Parameters.Add("@dateline", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@status", MySqlDbType.Int32, 0);
            cmd.Parameters.Add("@cover", MySqlDbType.VarChar, 255);

            cmd.Parameters["@isbn"].Value = info.Isbn;
            cmd.Parameters["@bookName"].Value = info.BookName;
            cmd.Parameters["@author"].Value = info.Author;
            cmd.Parameters["@translator"].Value = info.Translator;
            cmd.Parameters["@price"].Value = info.Price;
            cmd.Parameters["@language"].Value = info.Language;
            cmd.Parameters["@press"].Value = info.Press;
            cmd.Parameters["@pressDate"].Value = info.PressDate;
            cmd.Parameters["@editionNum"].Value = info.EditionNum;
            cmd.Parameters["@pressNum"].Value = info.PressNum;
            cmd.Parameters["@package"].Value = info.Package;
            cmd.Parameters["@paper"].Value = info.Paper;
            cmd.Parameters["@bookSize"].Value = info.BookSize;
            cmd.Parameters["@pages"].Value = info.Pages;
            cmd.Parameters["@words"].Value = info.Words;
            cmd.Parameters["@content"].Value = info.Content;
            var timeStamp = new DateTime(1970, 1, 1);
            var a = (DateTime.Now.Ticks - timeStamp.Ticks)/10000000;
            cmd.Parameters["@dateline"].Value = a;
            cmd.Parameters["@status"].Value = 1;
            cmd.Parameters["@cover"].Value = info.Cover;
           
            cmd.ExecuteNonQuery();
        }

        static void DownloadList()
        {
            var minPage = 1;
            var maxPage = 20;
            var urlTemplate = "http://list.china-pub.com/cache/browse2/19/{0}_1_19-02-04_0.html";
            var saveFile = string.Empty;
            for (var page = minPage; page <= maxPage; page++)
            {
                saveFile = string.Format("{0}\\list_{1}.html", savePath, page);
                DownloadPage(string.Format(urlTemplate, page), saveFile);
                Thread.Sleep(3000);
                Log(string.Format("downloading page {0}", page));
            }

        }

        static void DownloadPage(string pageUrl, string saveFile)
        {
            var uri = new Uri(pageUrl);

            using (var wc = new CGWebClient())
            {
                wc.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                try
                {
                    wc.DownloadFile(pageUrl, saveFile);
                }catch(Exception ex)
                {
                    Log(ex.Message);
                }
            }
        }

        static void Log(string str)
        {
            Console.WriteLine(str);
            using (var f = File.AppendText("log.txt"))
            {
                f.WriteLine(str);
            }
        }
    }

其它:BookInfo类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    public class BookInfo
    {
        public int Id { get; set; }
        public string Isbn { get; set; }
        public string BookName { get; set; }
        public string Author { get; set; }
        public string Translator { get; set; }
        public string Price { get; set; }
        public string Language { get; set; }
        public string Press { get; set; }
        public string PressDate { get; set; }
        public string EditionNum { get; set; }
        public string PressNum { get; set; }
        public string Package { get; set; }
        public string Paper { get; set; }
        public string BookSize { get; set; }
        public int Pages { get; set; }
        public int Words { get; set; }
        public int Dateline { get; set; }
        public int Status { get; set; }
        public string Cover { get; set; }
        public string Content { get; set; }
    }

CGWebClient类,封装了一下.net自带的WebClient类,有些web服务器,像amazon,如果client发送的Header里不包含ACCEPT,它就不返回数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    public class CGWebClient : WebClient
    {
        private System.Net.CookieContainer cookieContainer;
        private string userAgent;
        private int timeout;

        public System.Net.CookieContainer CookieContainer
        {
            get { return cookieContainer; }
            set { cookieContainer = value; }
        }

        public string UserAgent
        {
            get { return userAgent; }
            set { userAgent = value; }
        }

        public int Timeout
        {
            get { return timeout; }
            set { timeout = value; }
        }

        public CGWebClient()
        {
            timeout = -1;
            userAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)";
            cookieContainer = new CookieContainer();
            //cookieContainer.Add(new Cookie("example", "example_value"));
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            RefreshUserAgent();

            if (request.GetType() == typeof(HttpWebRequest))
            {
                ((HttpWebRequest)request).CookieContainer = cookieContainer;
                ((HttpWebRequest)request).UserAgent = userAgent;
                ((HttpWebRequest)request).Timeout = timeout;
            }
            return request;
        }

        private void RefreshUserAgent()
        {
            List<string> UserAgents = new List<string>();
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 8.0; AOL 9.5; AOLBuild 4337.43; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.34; Windows NT 6.0; WOW64; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618)");
            UserAgents.Add("Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8");
            UserAgents.Add("Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre (.NET CLR 3.5.30729)");
            UserAgents.Add("Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 GTB5 (.NET CLR 3.5.30729)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser; .NET CLR 2.0.50727; MAXTHON 2.0)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)");
            UserAgents.Add("Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; Media Center PC 3.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)");
            UserAgents.Add("Opera/9.70 (Linux i686 ; U; zh-cn) Presto/2.2.0");
            UserAgents.Add("Opera 9.7 (Windows NT 5.2; U; en)");
            UserAgents.Add("Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.8pre) Gecko/20070928 Firefox/2.0.0.7 Navigator/9.0RC1");
            UserAgents.Add("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7pre) Gecko/20070815 Firefox/2.0.0.6 Navigator/9.0b3");
            UserAgents.Add("Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8");
            UserAgents.Add("Mozilla/5.0 (Windows; U; Windows NT 6.0; ru-RU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16");
            UserAgents.Add("Opera/9.64 (X11; Linux x86_64; U; en) Presto/2.1.1");

            Random r = new Random();
            this.UserAgent = UserAgents[r.Next(0, UserAgents.Count)];

            UserAgents = null;
        }

    }